Polimorfismo

18 respostas
LuanMesquita

Olá galera,

quero saber porque não posso fazer isso.

Tenha uma classe chamada Conta:

public abstract class Conta {
	
	protected double $saldo;
	
	public void deposita(double $valor){
		
		this.$saldo += $valor;
        }

        public double getSaldo(){
		
		return this.$saldo;
	}

}

Tenho outra classe chamada ContaCorrente que extends de Conta:

public class ContaCorrente extends Conta{
	
        public void atualiza(double $taxa){
		
		this.$saldo += this.$saldo * $taxa * 2;
	    
        }
        
        public void deposita(double $valor){
    		
    		this.$saldo += $valor - 0.10;
    	}

}

Porquê não posso fazer isso...

public class Teste {

	public static void main(String[] args) {
		
		Conta $cp = new ContaCorrente();
		
		$cp.atualiza(0.1);
        }

}

Ou seja, criei uma variável do tipo Conta e atribui uma referência
a um objeto ContaCorrente. Estou tentando usar um método específico
que coloquei em ContaCorrente chamado atualiza, como vocês podem
vêr esse método não têm na classe Conta e acho que deve ser isso o problema
que o Eclipse está acusando. Queria que vocês me explicassem porque
ele reclama disso.

Vlw pessoal pela atenção.

Abração :D

18 Respostas

romarcio

Por que o método atualiza pertence apenas a ContaCorrente e não a Conta.
E no polimorfismo vc precisa ter o mesmo método nas duas classes ou então apenas na super classe. Os método pertencentes apenas a sub classe, vc só tem acesso se não usar polimorfismo.

0

I ae Luan , olha só no seu metodo principal aonde existe a classe abstrata , falta um objeto aonde voce vai fazer um tipo de Overrinding dele no proximo metodo que é a sua especializaçao aonde le vai estar implementado ,

ou seja voce pode criar como exemplo no seu codigo apos o seu public double getSaldo()

o seguinte

public abstract String getContaDeposito();

desta maneira , voce vai acrescentar no Conta , e sobrescrever o metodo nas suas especializaçoes …

por exemplo na classe ContaCorrente.

public abstract String getContaDeposito(); { return this.$saldo -= $valor - 10.0 ; }

ou seja voce vai sobreescrever o metodo na sua proxima especializaçao , espero que tenha ajudado um pouco rsrs

vlw abs.

(OBS: Voce vai sobreescrever o metodo conforme voce queira o retorno da sua classe.)

gRoOve

Uma pergunta, o que significa este sinal dólar $ na frente das suas variáveis?

0

entao grOove , ate onde eu sei nós podemos definir variaveis com uma inicial tambem sublinhado _ ou entao com um Cifrão , só nao sei se seria uma boa pratica definir variaveis desta forma …

mais é ate onde eu sei , agora o porque nosso amigo teve a ideia de usar desta forma , pode ser que tenha sido instruido por alguem …

mais ta valendo ^^ …

eu mesmo particularmente nao uso cifrao e nem underline …

flw abraço

carlos.e.a
gRoOve:
Uma pergunta, o que significa este sinal dólar $ na frente das suas variáveis?

Esse sinal nao quer dizer nada...é apenas o nome da variável que ele escolheu.

Como eu nao sei exatamente como é a sua aplicacao eu vou dar uma solucao que pode nao servir pra ela: voce pode resolver criando o metodo abstrato na superclasse...O problema é que voce vai ter que implementa-lo em todas as classes que estenderem Conta =D

Uma outra solucao mais elegante usando genéricos:

public abstract class Conta {
	
	protected double $saldo;
	
	public void deposita(double $valor){
		
		this.$saldo += $valor;
        }

        public double getSaldo(){
		
		return this.$saldo;
	}
    public abstract <T extends Number> void atualiza (T $saldo);

}
public class ContaCorrente extends Conta{
	
        public <T extends Number> void atualiza(T $taxa){
		
		this.$saldo += this.$saldo * $taxa.floatValue() * 2;
		
		
	    
        }
        
        public void deposita(double $valor){
    		
    		this.$saldo += $valor - 0.10;
    	}

}
public class Main {

	public static void main(String[] args) {
		
		Conta $cp = new ContaCorrente();
		
		$cp.atualiza(0.1);
        }

}

A vantagem:
Isso pode ser útil se as outras classes que implementam Conta utilizarem parametros "não-float". Já que voce vai ser obrigado a declarar "atualiza()" que seja pelo menos com um pouco mais de liberdade =D

Desvantagem:

Voce nao tem um controle rígido sobre os parametros...Se em um desses metodos atualiza voce "espera" um int e recebe um "double" vai haver truncamento.

Como por exemplo:

Suponha agora que o nosso método "atualiza()" agora espera um Inteiro. Logo, ao invés de fazer T.doubleValue() vamos fazer T.intValue(). Dessa forma:

public class ContaCorrente extends Conta{
	
        public <T extends Number> void atualiza(T $taxa){
		
		this.$saldo += this.$saldo * $taxa.intValue() * 2;
	    
        }
        
        public void deposita(double $valor){
    		
    		this.$saldo += $valor - 0.10;
    	}
}
Se fizermos isso:
public class Main {

	public static void main(String[] args) {
		
		ContaCorrente $cp = new ContaCorrente();
		$cp.deposita(500);
		$cp.atualiza(0.6);
		System.out.printf("%s",$cp.$saldo);
    }

}

Note que além do saldo não ter o valor esperado não temos nenhum aviso que teríamos se os parametros fossem rigidamente definidos.Porem eles nao poderiam ser rigidamente definidos se quisessemos ter polimorfismo. Temos entao um impasse...Cabe a voce avaliar e ver se vale a pena.Voce pode tambem verificar por reflexao que parametro o metodo recebeu e lançar uma excessao ou uma mensagem de erro caso esse parametro nao seja o esperado. Nesse caso sim teríamos um bom exemplo de bom uso de polimorfimos com genéricos.

LuanMesquita

Valeu Pessoal, vocês me ajudaram bastante.

Groove e 0r4cl3 uso o cifrão na frente das variáveis por causa
do php :smiley: quando comecei aprender php tinha um dificuldade
muito grande que era lembrar de colocar o $ na frente, depois
comecei entrar na paranóia por causa disso e depois meu professor
disse que eu podia colocar em java também, então deu no
que deu.

:smiley: Vlww Pessoal

x111
LuanMesquita:
Valeu Pessoal, vocês me ajudaram bastante.

Groove e 0r4cl3 uso o cifrão na frente das variáveis por causa
do php :D quando comecei aprender php tinha um dificuldade
muito grande que era lembrar de colocar o $ na frente, depois
comecei entrar na paranóia por causa disso e depois meu professor
disse que eu podia colocar em java também, então deu no
que deu.

:D Vlww Pessoal

mas tente evitar. Cada linguagem tem um padrão para declaração e identação. Em java as variaveis e os métodos começam com mínusculos. Isso acaba atrapalhando na hora que outras pessoas vão olhar o teu código.

Uma coisa que faltou falar é justamente o que é polimorfismo! Você está confundido um pouco com herança! Um erro comum.
Polimorfismo é capacidade de uma subclasse modificar o comportamento da classe base de modo que essa possa receber uma instancia dessa subclasse e quando for acessar esse seu comportamento vai acessar o comportamento da subclasse.

No seu caso você utilizou herança para adicionar um novo comportamento e não modificar um existente! A classe base não conhece esse novo comportamento!

Confuso né! vou dar um exemplo:
public class ExibeMensagem {
	public void exibir(){
		System.out.println("Mensagem");
	}
}
...

public class NovaMensagem extends ExibeMensagem {
	@Override
	public void exibir(){
		System.out.println("Nova mensagem");
	}
	
	public void novoComportamento(){
		System.out.println("Novo comportamento da classe ExibeMensagem");
	}
}
...
public class TesteExibeMensagem {
	public static void main(String[] args) throws IOException, AWTException, BackingStoreException {
		
		ExibeMensagem exibeMensagem = new ExibeMensagem();
		
		exibeMensagem.exibir();//Exibe o comportamento normal da classe para essa função
		
		exibeMensagem = new NovaMensagem();
		
		exibeMensagem.exibir();//Exibe o novo comportamento da classe para essa função

		exibeMensagem.novoComportamento(); //Erro não é um comportamento da classe ExibeMensagem, comente essa linha que funciona
		
		NovaMensagem novaMensagem = new NovaMensagem();
		
		novaMensagem.novoComportamento();//Agora sim exibe o próximo comportamento
}
joaodaniel

Eu entendo o polimorfismo como a capacidade de nos referirmos a um tipo de objeto, sendo específico somente até onde for necessário.

Esse caso da Conta é um ótimo exemplo.

Suponha que nosso programa tenha duas classes: ContaCorrente e ContaPoupanca. Se temos na classe Banco a necessidade de um método chamado adicionaConta, que irá receber como parâmetro a conta a ser adicionada no Banco, teríamos que criar na verdade dois métodos com sobrecarga, um recebendo como argumento a ContaCorrente e o outro recebendo como argumento a ContaPoupanca. Ou ainda dois métodos, um adicionaContaCorrente e outro adicionaContaPoupanca.

Percebe o código duplicado? Temos uma ação idêntica ocorrendo em duas classes, que apesar de próximas, têm pequena diferenças. Mas sabemos que ambas têm uma coisa em comum, correto? Tanto ContaCorrente como ContaPoupanca são Contas!

Portanto, se pudermos informar ao método adicionaConta que ele pode receber como argumento uma Conta, não importa se é ContaCorrente ou ContaPoupanca, desde que seja uma Conta, acabamos com esse problema de código duplicado. Isso é o polimorfismo! Podemos nos referir a diferentes tipos de objeto de uma única maneira. Isto é, não precisamos ser específicos demais, podemos "generalizar" e dizer que o argumento deve ser uma Conta.

Como isso vai ser feito, depende do propósito! Podemos usar herança para obter polimorfismo, mas também podemos usar interfaces.

Nesse caso da conta, acho que o ideal seria o uso de interface, pois a Conta não tem uma implementação padrão. Queremos apenas definir como deve ser uma Conta. Por exemplo, deve ter os métodos getSaldo, deposita, saca.

Por tanto, temos a Interface Conta, e ContaCorrente e ContaPoupanca implementam essa interface, e portanto podem ser referenciadas como uma Conta.

EDIT.: Um pouco de código sempre é bom:

Essa é a interface Conta. Repare que ela diz quais métodos as classes devem ter, mas não os implementam.
public interface Conta {

	void deposita(double valor);

	void saca(double valor);

	double getSaldo();

}
Aqui temos uma implementação, a ContaCorrente. Ela implementa os métodos da interface.
public class ContaCorrente implements Conta {

	private double saldo;

	public void deposita(double valor) {
		this.saldo += valor * 0.9;
	}

	public void saca(double valor) {
		this.saldo -= valor * 1.1;
	}

	public double getSaldo() {
		return this.saldo;
	}
}
E aqui temos uma outra implementação, a ContaPoupanca.
public class ContaPoupanca implements Conta {

	private double saldo;

	public void deposita(double valor) {
		this.saldo += valor;
	}

	public void saca(double valor) {
		this.saldo -= valor;
	}

	public double getSaldo() {
		return this.saldo;
	}
}
Agora, podemos nos referir a uma Conta, e chamar seus métodos, sem especificar o tipo de Conta.
public void adicionaConta(Conta c) {
//codigo
}
Esse método pode receber como argumento uma Conta. Não importa se é ContaCorrente ou ContaPoupanca, pois estamos usando Polimorfismo.
ViniGodoy

LuanMesquita:
Groove e 0r4cl3 uso o cifrão na frente das variáveis por causa
do php :smiley: quando comecei aprender php tinha um dificuldade
muito grande que era lembrar de colocar o $ na frente, depois
comecei entrar na paranóia por causa disso e depois meu professor
disse que eu podia colocar em java também, então deu no
que deu.

Só reforçando o que u X@ndy disse, em Java o padrão é tão forte que é oficial:
http://www.oracle.com/technetwork/java/codeconv-138413.html

Tente se acostumar com esse segundo padrão.
É sempre bom ser flexível e não reforçar ainda mais o vício (eu que programo em Java, C++ e C# que o diga).

x111

joaodaniel:
Eu entendo o polimorfismo como a capacidade de nos referirmos a um tipo de objeto, sendo específico somente até onde for necessário.

Esse caso da Conta é um ótimo exemplo.

Suponha que nosso programa tenha duas classes: ContaCorrente e ContaPoupanca. Se temos na classe Banco a necessidade de um método chamado adicionaConta, que irá receber como parâmetro a conta a ser adicionada no Banco, teríamos que criar na verdade dois métodos com sobrecarga, um recebendo como argumento a ContaCorrente e o outro recebendo como argumento a ContaPoupanca. Ou ainda dois métodos, um adicionaContaCorrente e outro adicionaContaPoupanca.

Percebe o código duplicado? Temos uma ação idêntica ocorrendo em duas classes, que apesar de próximas, têm pequena diferenças. Mas sabemos que ambas têm uma coisa em comum, correto? Tanto ContaCorrente como ContaPoupanca são Contas!

Portanto, se pudermos informar ao método adicionaConta que ele pode receber como argumento uma Conta, não importa se é ContaCorrente ou ContaPoupanca, desde que seja uma Conta, acabamos com esse problema de código duplicado. Isso é o polimorfismo! Podemos nos referir a diferentes tipos de objeto de uma única maneira. Isto é, não precisamos ser específicos demais, podemos “generalizar” e dizer que o argumento deve ser uma Conta.

Humm… antes eu pensava assim também, mas hoje não concordo. Para mim isso é só Herança. A meu ver Polimorfismo é capacidade de classe herdeira mudar o comportamento da classe pai. O Polimorfismo depende da herança, mas essa é independente. Um exemplo. Seria o do começo. A classe Conta não tinha a função atualizar! Derrepente isso não faça parte dela e seja um comportamento necessário apenas para a Classe ContaCorrente que extende a classe Conta afim apenas de adicionar esse novo comportamento!
Mesmo assim a classe Banco poderia receber, através do método adicionaConta, instancias das classes ContaCorrente e ContaPoupança. Isso evitaria o código duplicado, mas não teria a mudança de comportamento!
Isso a meu ver induz ao erro que do colega LuanMesquita, pois pode fazer creer que ele pode acessar os métodos das classes extendidas através das classes bases, quando isso não é possivel.

ViniGodoy

x@ndy:
Humm… antes eu pensava assim também, mas hoje não concordo. Para mim isso é só Herança. A meu ver Polimorfismo é capacidade de classe herdeira mudar o comportamento da classe pai. O Polimorfismo depende da herança, mas essa é independente. Um exemplo. Seria o do começo. A classe Conta não tinha a função atualizar! Derrepente isso não faça parte dela e seja um comportamento necessário apenas para a Classe ContaCorrente que extende a classe Conta afim apenas de adicionar esse novo comportamento!
Mesmo assim a classe Banco poderia receber, através do método adicionaConta, instancias das classes ContaCorrente e ContaPoupança. Isso evitaria o código duplicado, mas não teria a mudança de comportamento!
Isso a meu ver induz ao erro que do colega LuanMesquita, pois pode fazer creer que ele pode acessar os métodos das classes extendidas através das classes bases, quando isso não é possivel.

Não, o conceito que você explicou é o de sobreposição (overriding). Ele sim, permite que uma subclasse reescreva o comportamento da superclasse.

O conceito de polimorfismo é mesmo do método que recebe uma conta genérica. Veja bem nesse método, existe uma variável do tipo “Conta” que muda sua forma de trabalhar de acordo com o tipo concreto que ela esteja referenciando. Essa variável assume várias formas, e daí o nome de “polimorfismo” (de poli - muitos, mophos formas).

Seria até mesmo possível trabalhar com a classe Conta genérica, chamando os métodos da filha, sem que haja uma implementação nela, desde que haja uma declaração de método abstrato. Essa declaração indica que todas as contas são capazes de fazer uma determinada ação, embora exatamente como só pode ser determinado por um tipo específico de conta.

Herança já não tem nada a ver. Herança é a capacidade que classes tem de formar hierarquias de tipos. Interfaces fazem polimorfismo sem herança, assim como métodos delegate do C#. O polimorfismo estático (também chamado de sobrecarga) também é uma forma de polimorfismo sem herança.

x111

Humm… então era da forma que eu pensava antes, um professor me disse que estava errado ano passado! Ótimo saber que ele estava errado e não eu…rsrsrsrs. Como eu nunca encontrei uma boa definição fiquei com a dele!

Ai eu já não concordo contigo! Eu considero isso uma consequência da herança. A meu ver a finalidade da herança é extender o comportamento de uma classe e como consequência disso acabamos tendo uma hierarquias de tipos.

Isso era uma coisa que me incomodava no polimorfismo do modo dele, pois para mim uma classe implementa uma interface e não a extende!

ViniGodoy

A herança faz uso do polimorfismo. Ele é o mecanismo que permite que essa hierarquia seja criada, e a reforça. Mas já havia polimorfismo antes da herança. Em C++, por exemplo, você tem templates fazendo polimorfismo em tempo de compilação, sem herança. Algumas linguagens dinâmicas e de script dão há variáveis essa capacidade, seja através de tipos fracos, ou de duck typing.

Certamente a herança contribui para o polimorfismo. Na orientação à objetos, ela é o caminho mais natural para obte-lo. E ela é certamente um contexto onde ele faz um enorme sentido.

Exatamente. Na UML essa relação é chamada de realização. Não é exatamente certo dizer que uma classe “é filha de” de uma interface, nem que é um tipo especialista de uma interface. Uma classe simplesmente implementa uma interface, não a estende.

joaodaniel

VinyGodoy, me corrija se eu estiver errado, mas o Java precisa da herança e interface para “praticar” o polimorfismo, por ser uma linguagem fortemente tipada.
Em linguagens menos tipadas você pode simplesmente passar um argumento, e quem garante que o objeto terá o método que você está chamando é você. Correto?

ViniGodoy
joaodaniel:
VinyGodoy, me corrija se eu estiver errado, mas o Java precisa da herança e interface para "praticar" o polimorfismo, por ser uma linguagem fortemente tipada. Em linguagens menos tipadas você pode simplesmente passar um argumento, e quem garante que o objeto terá o método que você está chamando é você. Correto?

Sim, o duck typing e tipagem dinâmica é uma forma de polimorfismo, mesmo que mais insegura.

O C++ é fortemente tipado e também permite polimorfismo sem herança, através de templates.

Um exemplo disso é essa função aqui:
template<typename T> 
public T sum(T one, T two) {
   return one.plus(two);
}

No C++, os tipos one e two podem ter qualquer tipo, mesmo que venham de árvores não relacionadas, desde que seja possível usar sobre eles o método plus. Claro que do jeito que está, one e two também precisam ser do mesmo tipo.

Então, vamos supor que você tenha uma classe chamada Real, de números reais, e outra chamada Matrix, para matrizes. Como vcs imaginam, essas duas classes não tem qualquer relação de herança entre si. Mas para as duas, poderíamos ter um método add. Em C++, seria possível chamar essa função assim:
Real r1(10), r2(20);
Real r3 = sum(r1, r2);

Matrix m1 = new Matrix(
  0, 1,
  1, 0);

Matrix m2 = new Matrix(
  2, 2,
  3, 4);

Matrix m3 = sum(m1, m2);

No momento da compilação o compilador verificará se a execução da função é possível. Como tanto Real, quanto Matrix, em nosso exemplo, implementariam a função sum, retornando outro Real ou outra Matrix, o compilador irá gerar uma versão de cada função, para tipo onde ela se encaixa.

Caso não seja possível, o código não compila.

Note que no tipo T ali, houve polimorfismo. Para o programador existe apenas uma função.

x111

A herança faz uso do polimorfismo. Ele é o mecanismo que permite que essa hierarquia seja criada, e a reforça. Mas já havia polimorfismo antes da herança. Em C++, por exemplo, você tem templates fazendo polimorfismo em tempo de compilação, sem herança. Algumas linguagens dinâmicas e de script dão há variáveis essa capacidade, seja através de tipos fracos, ou de duck typing.

Certamente a herança contribui para o polimorfismo. Na orientação à objetos, ela é o caminho mais natural para obte-lo. E ela é certamente um contexto onde ele faz um enorme sentido.

Exatamente. Na UML essa relação é chamada de realização. Não é exatamente certo dizer que uma classe “é filha de” de uma interface, nem que é um tipo especialista de uma interface. Uma classe simplesmente implementa uma interface, não a estende.

Ops, acho que eu não me expressei bem! Quando disse que eu acredito que a hierarquia seja uma consequência da herança, não estava fazendo qualquer relação com o polimorfismo.
A meu ver a função principal da herança é estender o comportamento de uma classe e como consequência disto, a medida que vamos estendo mais classes para obter objetos cada vez mais especializados acabamos tendo uma hierarquia de classe. Não acredito que a função da herança seja ter uma hierarquia de classes!

Já a meu ver, não acredito que “a herança faz uso do polimorfismo”, pela relação acredito agora que seja ao contrário. (como meu parecer anterior sobre polimorfismo estava errado, no pensamento anterior tinha haver) na verdade o Polimorfismo pode se “utilizar” da herança, o que não acontece no caso do uso de interfaces. O polimorfismo se aplica de cima para baixo, uma classe mais abstrata “assume” o comportamento de uma classe mais especializada. Na herança é o contrário uma classe mais especializada acrescenta “funcionalidades” a uma classe mais abstrata.
Não sei se foi uma boa essa colocação de sequencia para explicação. Até por que a herança ocorre também de cima para baixo, eu quis colocar mais como um fluxo do comportamento!

ViniGodoy

Ah, agora entendi. Realmente acabei lendo coisas que você não escreveu. Acho que minha cabeça ainda estava no polimorfismo.

x111

Normal. Isso que é ruim de um fórum ou lista, a conversa fica truncada nunca é a mesma do que frente a frente!

Criado 20 de janeiro de 2011
Ultima resposta 22 de jan. de 2011
Respostas 18
Participantes 8