Solucionado (ver solução)
Solucionado
(ver solução)
17
respostas

Dúvida no Ex. 5 da Aula 1 - Herança, reescrita e polimorfismo

Queria ver se eu entendi certo. O Exercício pede para fazer a seguinte alteração,

de:

Conta c = new Conta();
ContaCorrente cc = new ContaCorrente();
ContaPoupanca cp = new ContaPoupanca();

para:

Conta c = new Conta();
Conta cc = new ContaCorrente();
Conta cp = new ContaPoupanca();

Porém se neste segundo caso, eu tiver um método somente na classe filha , como exemplo o metodo imprmir() e tentar acessar este método pela referencia de objeto cc , o códico não compila. Mas se eu tiver o método imprimir(), com o mesmo nome na classe pai , porem com resultados diferentes, ele irá apontar para o método imprimir() da classe filha , certo ?

Então resumindo, eu tenho que tomar cuidado ao instanciar objeto nesta forma Conta cc = new ContaCorrente() , pois se eu precisar utilizar um método que tenha SOMENTE na classe ContaCorrente e que não tenha na classe Conta , o código irá retornar um erro.

Agradeço se me disserem se entendi certo ou errado.

Obrigado.

17 respostas

Correto, utilizando polimorfismo você só tem acesso aos métodos e atributos, públicos, declarados na classe pai. Se você tentar utilizar um recurso existente somente na classe filha o seu código não irá compilar.

Filipe, Obrigado por responder.

Agora eu cheguei a fazer o inverso. Criei o método somente na classe pai e tentei utilizar, mas deu erro tbm. O código só compila se eu tiver o método nas duas classes.

Exemplo:

class Conta{

    public void imprimir(){
        System.out.println("Teste1");
    }

}
class ContaCorrente extends Conta{

    public void imprimir(){
        System.out.println("Teste2");
    }

}
class TesteContas{
    public static void main(String[] args){

        Conta c = new Conta();
        Conta cc = new ContaCorrente();

        cc.imprimir();
    }
}

Então pelo que entendi eu sou meio que obrigado a ter o mesmo método nas duas classes , mas quando eu for chamar o método, ele irá apontar para o método da classe ContaCorrente por causa do new ContaCorrente.

Você importou corretamente as classes, pois deveria dar certo? Qual foi o erro?

Neste exemplo que citei , não da erro. O erro só ocorre qdo eu tiro o método de uma das classes (Conta e ContaCorrente). Por isso que eu disse que sou obrigado a colocar os métodos iguais nas duas classes. Pois se eu tirar o método , seja da classe Conta ou da classe ContaCorrente, o código não compila. Eu quero só comprovar com alguém mais experiente se é isso mesmo que ocorre. Se é regra ter que criar os métodos iguais para usar dessa forma.

No seu caso, se você tirar o método da classe ContaCorrente o código deve compilar, porém se você tirar o imprimirda classe Conta deve dar um erro de compilação.

Isso mesmo Filipe !! Acabei de testar e foi isso mesmo que aconteceu.

O que não entendi é o pq disso acontecer. Se eu colocar o método imprimir() nas duas classes, a referencia do objeto é apontada para o método da classe ContaCorrente. Porem se eu apagar o método da classe Conta , que não foi referenciado no teste que acabo de citar, o código da erro.

Já que ele (O método imprimir da classe Conta) não esta sendo usado, porque da erro se eu tirar ?

Não sei se entendeu minha duvida. Caso ficou estranho eu tento explicar de outra forma.

Obrigado

Quando você utiliza o polimorfismo, como eu disse anteriormente, somente os métodos e atributos da classe pai podem ser utilizados. Se você tirar o método imprimir da classe Conta não importa se há uma implementação dele nas classes filhas pois a sua útltima instância, no caso Conta cc = new ContaCorrente()é interpretado Conta e não uma ContaCorrente.

Bom dia,

Galera vamos tentar esclarecer melhor isso.

Quando você utiliza Herança, todas as subclasses de uma superclasse herdarão atributos e comportamentos. Com isso, se temos uma superclasse Conta, que tem um método imprimir, todas as filhas de Conta poderão utilizar este método, e não é necessário que imprimir seja escrito nas classes filhas.

Se você apaga o método imprimir de Conta, a superclasse não tem mais o comportamento, logo não tem como as subclasses herdarem. Com isso, se você vai e escreve imprimir na ContaCorrente por exemplo, é um comportamento apenas dela, a superclasse Conta não tem como conhecer. Não é bidirecional esta relação. Um classe filha sabe o que a classe pai faz, mas a classe pai não sabe nada do que as classes filhas fazem ok.

Quanto ao Polimorfismo, vamos ser claros:

Conta cc = new ContaCorrente();

Isso não é interpretado como Conta. O Polimorfismo permite que você referencie (a expressão à esquerda é apenas a variável de referência) um objeto como outro (através da Herança e da implementação de Interfaces). Mas em tempo de execução, a instância criada é de um objeto ContaCorrente, logo por mais que a referência seja de Conta, tudo que o objeto ContaCorrente for capaz de fazer, ele conseguirá fazer.

Caso tenha ficado alguma dúvida, rodem este exemplo:

public class Conta {
    private int numero;
    private int agencia;
    private double saldo;

    public Conta(int numero, int agencia, double saldo) {
        this.numero = numero;
        this.agencia = agencia;
        this.saldo = saldo;
    }

    public void imprimir() {
        System.out.println("Isso é uma CONTA!");
    }
}
public class ContaCorrente extends Conta {
    private String nome;

    public ContaCorrente(int numero, int agencia, double saldo, String nome) {
        super(numero, agencia, saldo);
        this.nome = nome;
    }

    @Override
    public void imprimir() {
        System.out.println("Isso é uma CONTA CORRENTE!");
    }
}
public class Teste {
    public static void main(String[] args) {

        Conta c = new Conta(1, 11, 100);
        c.imprimir();

        Conta cc = new ContaCorrente(2, 22, 200, "Joao");
        cc.imprimir();
    }
}

Aqui vemos claramente, que independente da variável de referência ser do tipo Conta (Polimorfismo permite isso), em tempo de execução a instância criada é um objeto do tipo ContaCorrente, e usará os comportamentos que a classe ContaCorrente especificar. Logo, não utiliza o método imprimir da superclasse Conta, mas sim o seu próprio.

Não confundam achando que o tipo da variável de referência faz uma instância de um objeto ser tratado como outro blz.

Qualquer dúvida fiquem a vontade.

Abraços e bons estudos.

Minha explicação não ficou clara. Quando eu disse que Conta cc = new ContaCorrente() é interpretado como Conta e não ContaCorrente o que eu pretendia dizer era justamente a questão da referência. A instância será a da classe ContaCorrente, porém somente os métodos e atributos que estão declarados na classe pai poderão ser utilizados por estar referenciada como uma Conta.

Opa aí sim, agora ficou certo.

Ficou com alguma dúvida Thiago?

Poxa pessoal...vcs são feras....Deu pra entendeu muito mais agora.

Só me restou uma duvida. Se eu tenho essas duas expressões

Conta cc = new ContaCorrente();

e

ContaCorrente cc = new ContaCorrente();

Significa que as duas estão referenciadas para a subclasse ContaCorrente, porem a variável da primeira é do tipo Conta e a variável da segunda é do tipo ContaCorrente. Em outras palavras, a primeira é uma Conta , porem apontada para ContaCorrente e a segunda é uma ContaCorrente que por ser filha de Conta, tbm é uma Conta. É isso ?

Novamente Obrigado e me desculpe tanta confusão....ja tentei estudar Polimorfismo antes e dá um nó na cabeça ...kkkkkk

Ambas são instâncias de ContaCorrente, mas a primeira é referenciada como Conta, polimorfismo, e a segunda é referenciada como ContaCorrente.

Mas deu pra ver que você entendeu Thiago. Os conceitos de OO não são fáceis mesmo, e quando se começa a aprender é normal dar um nó na cabeça.

Tem muito professor de superior que não sabe explicar direito e deixa turmas inteiras boiando!! kkkkk

Bons estudos, abraço.

Eu entendi o poder de Herença e as formas que ela pode ter , usando o polimorfismo (Superclasses e Subclasses). Mas ainda não consegui entender a diferença entre as expressões

Conta c = new ContaCorrente();
ContaCorrente = new ContaCorrente();

Se as duas são instanciadas como ContaCorrente, qual devo usar e em quais circunstancias ? pq não usar somente o ContaCorrente c = new ContaCorrente(); Essa duvida que esta bugando minha mente.

solução!

Basicamente se você for escrever um comportamento que trabalhará com qualquer tipo de conta, o parâmetro terá que ser Conta, então tanto faz se você passar como argumento na chamada deste método uma ContaCorrente, uma ContaPoupanca, todas são contas.

Agora dentro da programação deste método, você não conseguirá chamar diretamente algo que é próprio da ContaCorrente ou da Poupanca, porque como o argumento é generalizado pelo tipo da Superclasse, você só poderá usar o que a superclasse conhece. Se não fosse assim, a JVM não teria como garantir que você vai receber uma Conta (podendo ser qualquer subclasse de Conta), e que vai usar atributos ou métodos que todas as contas tenham.

Ou seja, como a JVM deixaria você chamar conta.metodoEspecificoContaCorrente a partir de uma referencia de Conta, sendo que se o argumento fosse uma ContaPoupanca este método não existira, causando um erro. Ficou mais claro? A JVM, para garantir que isso não dará erro, permite o polimorfismo da Orientação a Objeto, mas em contrapartida te limita a chamar apenas os comportamentos que é 100% de certeza que todas subclasses de Conta tem, sendo assim, apenas o que a referência do tipo Conta possui. Um exemplo rápido do que estamos falando:

class AtualizadorContas {
    private double taxa = 0.05;

    // metodo que atualizará o saldo de todas as contas com correção de uma taxa de CDI
    public void atualizarSaldoConta(Conta c) {
        c.atualizar(this.taxa);
    }
}

// Na classe Conta
class Conta {
    //atributos e construtores omitidos

    public atualizar(double taxa) {
        this.saldo += this.saldo * taxa; 
    }    

    // métodos omitidos
}

O objetivo aqui é generalizar, para ficar mais fácil de dar manutenção no código, o chamado desacoplamento. Espero que tenha ajudado um pouco.

Nós podemos explicar mais aqui, mas nas próximas aulas essas coisas serão explicadas também e com a prática cada vez se entende melhor. Quando o curso começar a exemplificar mais o polimorfismo você conseguirá visualizar melhor.

Sugiro avançar no curso, e se mais para frente ficar na dúvida abra novo tópico.

Abraços.

Evita o acoplamento. Se você precisar manipular uma conta, independente se ela for ContaCorrente ou ContaPoupanca, é possível criar um único método que receba uma Conta. Sendo assim qualquer classe que seja filha de Conta pode ser passada para esse método.

Entendi agora Emerson e Filipe. Fez todo sentido agora com essas ultimas explicações.

Quero agradecer aos dois pela paciência e pelo tempo destinado as minhas duvidas. Muito obrigado mesmo ! Vou continuar nos estudos aqui e praticar mais.

Abraços