Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
11
respostas

Dúvida sobre instância de objetos e polimorfismo

Olá a todos, tudo bem? Eu fiquei com uma dúvida bem básica, mas que ainda não me desceu muito bem. Vamos lá.

Eu tenho uma classe chamada Funcionário e ela serve para ser herança para a classe Gerente, Técnico e Engenheiro. Até ai tudo ok, se eu quero instanciar alguém Gerente g1 = new Gerente( ); ou Técnico t1 = new Técnico( );

Porém, também é possível realizar Funcionário g1 = new Gerente( ); Muito bem, se eu crio Gerente g1 = new Gerente( ); eu herdo tudo de Funcionário e além disso ganho os atributos e métodos de Gerente, mas declarando como Funcionário g1 = new Gerente( ); eu não consigo acessar as peculiaridades de Gerente. Então, por que criar assim Funcionário g1 = new Gerente( ); se é limitado e não sempre assim Gerente g1 = new Gerente( ); que é mais amplo?

11 respostas

Bom dia.

Com relação à sua dúvida, entendo que seja uma questão de Herança, não de Polimorfismo. Vamos aos conceitos:

Herança: Uma classe pode ter várias filhas, mas pode ter apenas uma mãe. Seria o uso do "extends". A classe Gerente herda todos os atributos e métodos da classe mãe, no nosso caso, a Funcionario. Ela também herda os atributos e métodos privados, porém não consegue acessá-los diretamente.

Polimorfismo: seria a Reescrita de método. Imagine que sua classe Funcionário contém a função:

public double getBonificacao() { return this.salario * 0.10; }

Na classe Gerente, podemos implementar a mesma função, mas com código diferente:

public double getBonificacao() { return this.salario * 0.13; }

Na sua dúvida, ao criar Funcionário g1 = new Gerente( ); , sua variável 'g1' é uma classe do tipo 'Funcionário'. Por isso você só consegue acessar os métodos dessa classe. Gerente extende de Funcionário, mas a definição do tipo da variável é que vale.

Quando você cria: Gerente g1 = new Gerente( ); , sua variável 'g1' é uma classe do tipo 'Gerente' , e que extende de Funcionário. . Por isso você consegue acessar todos métodos das duas classes.

Um gerente sempre é um funcionário, mas nem todo funcionário é um gerente, certo?

Ok! Mas ainda fico na dúvida, por que da erro ao criar Gerente g1 = New Funcionário( ); E também, por que criar um Funcionário f1 = new Gerente( ) se não consigo acessar os métodos de gerente assim? Não seria mais certo usar apenas sempre Gerente g1 = new Gerente( ); ? Precisava de um exemplo prático disso para tentar fixar essa ideia, por que nesse momento não consigo achar utilidade em Funcionario f1 = new Gerente( );

Vamos lá...

Realmente, Funcionario f1 = new Gerente( ); = não tem utilidade - você está correto no seu pensamento; não dá erro de codificação, mas é um erro conceitual. Você não vai acessar as propriedades de Gerente, pois sua variável é um Funcionário.

O mais certo é:

Gerente g1 = new Gerente ( );

Técnico t1 = new Técnico( );

Thiago, tentando criar Gerente g1 = new Funcionario( ); o código apresenta erro. Diz que não pode converter gerente para funcionário.

Bom dia!

Você tem razão. Errei no momento da digitação , copiei e colei mas não corrigi. Já arrumei o post anterior.

Realmente dá erro.

Obrigado mesmo assim Thiago!

Bons estudos!

Quando puder coloque o tópico como resolvido , para dar baixa no fórum.

É que ainda estou com a dúvida, vamos ver se alguém aparece para sanar essa questão da vantagem de criar Funcionario f = new Gerente( ); ao invés de apenas Gerente g = new Gerente( ); Se ninguém aparecer até sábado eu fecho. Obrigado pela atenção!

solução!

Se você ainda tem dúvida, não encerre a task.

Ainda não achei um lugar com relação à questão da vantagem de criar Funcionario f = new Gerente( ); . Se suas dúvidas surgiram da aula: https://cursos.alura.com.br/course/java-heranca-interfaces-polimorfismo/task/35076, lá o professor fala somente de Gerente g = new Gerente( );

O Gerente é um Funcionário, por essa razão ele tem direito a todas as propriedades contidas em Funcionário além de suas próprias. Se ele não tivesse acesso a alguma propriedade, então gerente não seria um funcionário, eles poderiam ser até parecidos, mas um não extende do outro.

O exemplo do curso o Funcionário de fato não consegue utilizar os atributos, porque por mais que eles sejam parecidos, de fato, um funcionário não é um gerente

Exato! Acho que nesse momento vou ficar sem resposta, mas obrigado pela ajuda Thiago!

Oi Felipe, tudo bem? Eu gostaria de fazer uma adição.

É bem verdade que se você fizer Funcionario g1 = new Gerente() você não acessa as coisas específicas do gerente por que o compilador em tempo de compilação, não consegue garantir que o objeto g1 é um gerente, neste caso, o tipo da esquerda é quem garante a tipagem.

Quando você faz isso, neste caso, você está sendo mais amplo por que você está usando o supertipo do Gerente. O Gerente é uma especialização do Funcionário e por isso não dizemos que o Gerente é mais amplo. E sim mais específico.

A última questão é: por que você faria isso? Puro polimorfismo.

Imagine que você deseje calcular a bonificação do salário de acordo com o cargo (se cada cargo tem um tipo, fica fácil), neste caso, você teria o cálculo da bonificação direto dentro do Funcionario e sim, eu entendo que gerente também possua esse método por herança, mas lembra da especialização? Ótimo, neste caso cada subclasse poderia sobrescrever o método e ter sua própria lógica de cálculo da bonificação. Porém, em algum lugar você iria querer processar esse valor sem precisar fazer uma regra de criar o objeto do tipo específico Gerente ou Engenheiro. Então você poderia ter algo que funcione da seguinte forma:

public void defineNovoSalario(Functionario f) {
    f.setSalario(f.calculaAumento());
}

Esse método funciona tanto para objetos do tipo Funcionário, quanto Gerente, quanto Engenheiro. Ele é do supertipo, então o subtipo também é aceito na parametrização. Claro, os métodos precisam estar no supertipo, mas os valores da conta, serão usados do subtipo informado.

Posso ter bagunçado a explicação, mas é por ai. Ah, isso também funciona com Interfaces, então pode ser muito comum você ver por ai coisas nessa linha:

List<String> nomes = new ArrayList<>();

Por que neste caso, só nos importamos com o que uma lista é capaz de fazer, não nos preocupamos se é um ArrayList ou qualquer outro tipo de lista.

É o que falamos sobre programar voltados para a interface e não para a implementação. Importa o que o objeto faz e não o que ele é de fato.