Ainda não tem acesso? Estude com a gente! Matricule-se
Ainda não tem acesso? Estude com a gente! Matricule-se

Solucionado (ver solução)

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.