Dúvida Básica sobre OO - Instancias

Estava eu aqui, lendo um artigo em C++… quando me bateu uma dúvida …

Qual a diferença de se fazer isso:

Pessoa p = new Fisica();

invés de:

Fisica f = new Fisica();

Sendo, claro, que: Fisica extends Pessoa

Valeu galera, posso ter viajado um pouco, mas é o sono!

fui!

Eh… tenho a mesmíssima dúvida faz tempo. Tá complicado entender qual é a real utilidade disso. Sei que é por causa do poliformismo… e só isso que sei! :lol:

Nesse seu caso, p só vai poder usar os métodos e variáveis encontrados em Pessoa , mesmo ele sendo instanciado como Fisica , não é?

Então, pra mim que sou iniciante no assunto, realmente não parece ter muito sentido… já que é pra somente usar os métodos encontrados na classe Pessoa não seria mais fácil instancia-lo logo como Pessoa ?

Sinto falta de um exemplo mais prático sobre isso nos livros que leio. Quando vão abordar esse tema eles sempre usam o exemplo de um array que pode receber tanto Pessoa como Fisica , mas realmente não consigo entender com esse tipo de exemplo.

Bom, a primeira utilidade é criar um método que seja útil para qualquer pessoa:

public float calculaAumento(Pessoa pessoa) { return pessoa.valorRecebivel() * 1.5; }
Esse método agora pode ser chamado por uma pessoa física ou jurídica.

No caso de uma declaração direta, haverá vantagem quando você tem uma classe em que os métodos do pai são mais usados, existem diversas implementações filhas, mas essas implementações adicionam pouca funcionalidade. Então, você pode usar essa técnica para trocar de implementação a hora que quiser.

É o caso da classe Lista. Se vocês lembrarem da matéria de estrutura de dados, existem vários tipos de lista: ListaEncadeada, ListaCircular, ListaEstatica, etc. Todas essas listas fazem basicamente a mesma coisa: Permitem adicionar um objeto, retirar um objeto, acessa-lo pelo índice. Por isso, temos a super classe Lista, e as classes filhas ListaEncadeada, ListaCircular, ListaEstatica, etc…

Pois bem, agora imagine que você vai fazer uma classe que usa uma lista de pessoas. Na hora que você analisou o sistema, percebeu que seriam grupos de no máximo 10 pessoas. Então decidiu usar uma lista circular.

Por exemplo, a classe abaixo:

public class Grupo {
   private String nome;
   private ListaCircular pessoas = new ListaCircular(10);

   public float totalPagoParaOGrupo() {
      float total = 0;
      for (int i = 0; i &lt pessoas.tamanho(); i++)
         total += pessoas.get(i).valorRecebivel();
      }
   }

   public ListaCircular pegarMaiores() {
      ListaCircular maiores = new ListaCircular(10);
      for (int i = 0; i &lt pessoas.tamanho(); i++)
         if (pessoas.get(i).idade &gt 18)
            maiores.add(pessoas.get(i);
      return maiores;
   }
}

Ok, o tempo passou e a sua classe grupo funciona maravilhasamente bem. Você calcula o valor pago ao grupo facilmente, e retira os maiores do grupo com a mesma facilidade. É o caso dessa classe festa, que está se beneficiando da classe Grupo:

public class Festa {
     Grupo participantes = null;

      public SalaoDeFestas(String nomeDoGrupo) {
         participantes = GrupoDao.carregarDoBanco(nomeDoGrupo);
      }
      //Retorna verdadeiro se 80% ou mais forem maiores.
      public boolean eUmaFestaParaAdultos() {
         ListaCircular maiores = parcipantes.pegarMaiores();
         return maiores &gt (parcipantes * 0.8); //80% ou mais
      }
}   

Um belo dia, seu chefe diz que fecharam contrato com outra empresa, e que agora terão grupos maiores de tamanhos iguais a 100, 200 ou até 1000 pessoas!! O tamanho é desconhecido e não tem cabimento aumentarmos nossa fila circular para um tamanho grande para nos garantirmos, pois isso ocuparia muita memória. Surge então a idéia de substituirmos por uma lista encadeada. Afinal listas encadeadas crescem a medida em que adicionamos elementos e nunca tem problemas de memória.

O que acontece com a sua classe?

Bem, já vimos que a fila circular não vai servir mais… Agora, prestem atenção no seguinte. Todos os locais do programa que utilizam o método pegarMaiores, também foram obrigados a utilizar uma ListaCircular. Como no caso da classe festa.

Bem, qual a dificuldade de se trocar para uma lista encadeada? Teríamos que alterar não só a classe Grupo, mas também a classe Festa e todas as outras classes que utilizassem o método pegarMaiores(). A situação seria completamente diferente se tivéssemos feito:

[code]
public class Grupo {
private String nome;
private Lista pessoas = new ListaCircular(10);

public float totalPagoParaOGrupo() {
float total = 0;
for (int i = 0; i &lt pessoas.tamanho(); i++)
total += pessoas.get(i).valorRecebivel();
}
}

public Lista pegarMaiores() {
Lista maiores = new ListaCircular(10);
for (int i = 0; i &lt pessoas.tamanho(); i++)
if (pessoas.get(i).idade &gt 18)
maiores.add(pessoas.get(i);
return maiores;
}
}[/code]

[code]public class Festa {
Grupo participantes = null;

  public SalaoDeFestas(String nomeDoGrupo) {
     participantes = GrupoDao.carregarDoBanco(nomeDoGrupo);
  }
  //Retorna verdadeiro se 80% ou mais forem maiores.
  public boolean eUmaFestaParaAdultos() {
     Lista maiores = parcipantes.pegarMaiores();
     return maiores &gt (parcipantes * 0.8); //80% ou mais
  }

}[/code]

Notem que agora, para trocar o tipo de lista, bastaria irmos para os locais onde as listas são criadas na classe Grupo e trocarmos por uma lista encadeada:

[code]public class Grupo {
private String nome;
private Lista pessoas = new ListaEncadeada();

public float totalPagoParaOGrupo() {
float total = 0;
for (int i = 0; i &lt pessoas.tamanho(); i++)
total += pessoas.get(i).valorRecebivel();
}
}

public Lista pegarMaiores() {
Lista maiores = new ListaEncadeada();
for (int i = 0; i &lt pessoas.tamanho(); i++)
if (pessoas.get(i).idade &gt 18)
maiores.add(pessoas.get(i);
return maiores;
}
}[/code]

Não é mais necessário alterar a classe festa. Por que? No segundo caso, não criamos nenhum tipo de dependência sobre qual tipo de lista a classe festa receberia. Mesmo porque, para o contexto do sistema, também não interessava. Qual seria, afinal, a utilidade da classe Festa saber que a lista de pessoas é Encadeada ou Circular? Nenhuma.

Para festa, o que interessa é ter uma Lista qualquer de pessoas participantes. Outras classes que utilizassem o método pegaMaiores da classe Grupo também não seriam afetadas, pelo mesmo motivo.

A modificação do sistema é menor, menos traumática, pois ocorre num só ponto (a classe Grupo). Notem também que agora o problema ficou restrito a única classe que foi modelada errada (Grupo) e não ao sistema todo. O fato de termos usado uma lista mais genérica, não fez com que o problema se “espalhasse” para o resto do código (classe Festa).

Em resumo, você fica livre para escolher a implementação que quiser, a hora que quiser, altera-la no futuro, sem que o resto do sistema seja afetado.

então,

o pensamento meu sobre isso… fica naquela de…

se P é uma nova pessoa FISICA

assim como F tbm é, pois fisica extends pessoa

ou seja… é a mesma coisa , não?

Viny, vou dar uma estudada no que vc postou!

Ok, se ainda tiver dúvida, é só entrar em contato! :wink:

Bom, acho que a palavra chave nessa dúvida se chama POLIMORFISMO…

vc tem um metodo que esta declarado em Pessoa, e está sobrescrito (vide diferença entre sobrescrito e sobrecarregado) em PessoaFisica e PessoaJurídica… nao é um exemplo tao bom, pq so existem 2 classes concretas… poderiam haver 20…

O caso é: qdo vc faz Pai = new Herdeiro(), vc está lidando apenas com objetos Pai… qdo vc faz pai.metodo(), em tempo de execução a JVM sabe que, na verdade, aquele “Pessoa”(pai) é na verdade um “PessoaFisica”(herdeiro), ou “PessoaJuridica”(herdeiro)… e vai executar o método que está sobrescrito na classe herdeira…

sacou? é como se a JVM te poupasse de ter q fazer uns testes do tipo

if (Carro instance of Ferrari) (Ferrari)Carro.acelerar();
if (Carro instance of Porshce) (Porsche)Carro.acelerar();
if (Carro instance of Mazda) (Mazda)Carro.acelerar();
if (Carro instance of Fusca) (Fusca)Carro.acelerar(); //deprecated =P

princípio básico de herança…

ViniGodoy…
Outra duvida… Na sua classe Grupo ao inves de eu dar :

Lista pessoas = new ListaEncadeada();
public ListaEncadeada pegarMaiores() ;
// na classe Festa
public boolean eUmaFestaParaAdultos() {
         ListaCircular maiores = parcipantes.pegarMaiores();
}

Eu usar:

ListaEncadeada pessoas = new ListaEncadeada();
public Lista pegarMenores();
// na classe Festa
public boolean eUmaFestaParaAdultos() {
         Lista maiores = parcipantes.pegarMaiores();
}

Não vai ter o mesmo sentido???
Só que agora eu usei o :

Filha f = new Filha();
// ao inves de :
Pai f = new Filha();

E no metodo que retorna uma filha eu pedi pra retornar pai.

A partir disso que eu disse. Qual a praticidade que eu tenho em
Pai f = new Filha();

Agradeço…

[quote=peerless]Estava eu aqui, lendo um artigo em C++… quando me bateu uma dúvida …

Qual a diferença de se fazer isso:

Pessoa p = new Fisica();

invés de:

Fisica f = new Fisica();

Sendo, claro, que: Fisica extends Pessoa

Valeu galera, posso ter viajado um pouco, mas é o sono!

fui![/quote]

quando vc faz isso:

Pessoa p = new Fisica();

vc tem acesso ao metodos que foi subscrito da class que está apos =…


class Animal{
	public void eat(){
	System.out.println("class generic");
	}
}

public class Cavalo extends Animal{
	public void eat(){
		System.out.println("raça mangalarga");
		}
	}
	
	class AnimalTest{
		public static void main(String args[]){
		Animal a = new Animal();
		Animal b = Cavalo();
		a.eat();
		b.eat();
		}
	}

[quote=omaisnormalbaba]ViniGodoy…
Outra duvida… Na sua classe Grupo ao inves de eu dar :
Não vai ter o mesmo sentido???
[/quote]

Vai ter o mesmo sentido sim. Entretanto, se você começar usar métodos que são específicos da lista encadeada, você não conseguirá trocar a implementação tão facilmente no futuro.

Você só deve declarar assim, se esses métodos forem imprescindíveis para a sua classe. Caso contrário, com esse tipo de classe, é melhor manter o uso genérico, justamente para que a implementação possa ser substituída no futuro.

Referenciando o tipo pelo pai, você tem a garantia de que o Java não deixará você utilizar métodos específicos da classe filha e, portanto, não corre o risco de ter que reescrever alguma coisa.

[quote=ViniGodoy][quote=omaisnormalbaba]ViniGodoy…
Outra duvida… Na sua classe Grupo ao inves de eu dar :
Não vai ter o mesmo sentido???
[/quote]

Referenciando o tipo pelo pai, você tem a garantia de que o Java não deixará você utilizar métodos específicos da classe filha e, portanto, não corre o risco de ter que reescrever alguma coisa.[/quote]

Valeu… Intão é legal eu fazer desta forma para que eu fique restrito a metodos que são declarados na classe pai e subscritos de acordo com a necessidade na classe filha… È claro que cada caso é um caso…