12
Disciplina: Linguagem de Programação Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc. Notas de Aula 04: Herança e polimorfismo. Objetivos da aula: Introduzir o conceito de Heranças Criar uma hierarquia de classes Rever o uso de modificadores de acesso. Apresentar o conceito de polimorfismo Aprender e utilizar o operador super Recordando... Nas aulas passadas criamos uma classe Carro, que apresentava alguns atributos (cor, marca, estadoMotor) e alguns métodos (acelerar, desacelerar, etc). Se quiséssemos criar uma outra classe Caminhao, certamente várias características, tanto atributos como comportamentos (métodos), seriam parecidos ou mesmo iguais aos da classe Carro. Caso semelhante ocorreria se quiséssemos criar um carro elétrico. Nestas situações, ao invés de criarmos uma série de classes, desvinculadas umas das outras, mas com várias coisas em comum, podemos utilizar um conceito que permite criar uma relação entre as classes. Herança O conceito de herança permite estabelecer uma hierarquia entre as classes. É um mecanismo que permite a uma classe herdar as operações e os atributos de outra classe. Com o uso da herança, quando se escreve uma classe é necessário especificar apenas no que ela é diferente da classe-mãe, classe-base ou superclasse. O mecanismo da herança dá acesso automático às informações contidas na classe base. Através da herança, uma classe possui imediatamente toda a funcionalidade de uma classe já existente. A classe que herda características da outra é chamada subclasse ou classe-filha e a classe que fornece a herança é chamada superclasse ou classe-mãe. A herança é, portanto, o compartilhamento de atributos e operações entre classes, baseado em um relacionamento hierárquico do tipo pai-filho (ou mãe-filho), em que a classe pai possui definições que podem ser utilizadas nas classes filhas. Estas funcionam como uma especialização da classe pai (as

Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Notas de Aula 04: Herança e polimorfismo.

Objetivos da aula: Introduzir o conceito de Heranças

Criar uma hierarquia de classes

Rever o uso de modificadores de acesso.

Apresentar o conceito de polimorfismo

Aprender e utilizar o operador super

Recordando... Nas aulas passadas criamos uma classe Carro, que apresentava alguns atributos (cor, marca, estadoMotor) e alguns métodos (acelerar, desacelerar, etc). Se quiséssemos criar uma outra classe Caminhao, certamente várias características, tanto atributos como comportamentos (métodos), seriam parecidos ou mesmo iguais aos da classe Carro. Caso semelhante ocorreria se quiséssemos criar um carro elétrico. Nestas situações, ao invés de criarmos uma série de classes, desvinculadas umas das outras, mas com várias coisas em comum, podemos utilizar um conceito que permite criar uma relação entre as classes.

Herança O conceito de herança permite estabelecer uma hierarquia entre as classes. É um mecanismo que permite a uma classe herdar as operações e os atributos de outra classe. Com o uso da herança, quando se escreve uma classe é necessário especificar apenas no que ela é diferente da classe-mãe, classe-base ou superclasse. O mecanismo da herança dá acesso automático às informações contidas na classe base. Através da herança, uma classe possui imediatamente toda a funcionalidade de uma classe já existente. A classe que herda características da outra é chamada subclasse ou classe-filha e a classe que fornece a herança é chamada superclasse ou classe-mãe.

A herança é, portanto, o compartilhamento de atributos e operações entre classes, baseado em um relacionamento hierárquico do tipo pai-filho (ou mãe-filho), em que a classe pai possui definições que podem ser utilizadas nas classes filhas. Estas funcionam como uma especialização da classe pai (as

Page 2: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

filhas estendem a sua utilização básica da classe pai para outras utilizações mais especializadas).

A seguir uma figura ilustrativa do conceito de herança.

Uma superclasse direta é a superclasse a partir da qual a subclasse herda explicitamente. Uma superclasse indireta é qualquer superclasse acima da superclasse direta. Desta forma, no exemplo acima, Mamífero é uma superclasse direta de SerHumano e Animal é uma superclasse indireta de SerHumano.

Herança em Java Todas as classes, tanto as que são escritas pelo programador quanto aquelas da biblioteca de classes do Java, são organizadas em uma hierarquia. No topo da hierarquia de classes está a classe Object. Todas as classes são herdeiras desta superclasse. Object é a classe mais geral da hierarquia, ela define o comportamento herdado por todas as classes da hierarquia de classes do Java.

Em Java, uma classe pode ter apenas uma superclasse (Java não suporta herança múltipla, como a existente em C++, por exemplo), mas cada classe pode ter um número ilimitado de subclasses. Se a superclasse tiver comportamentos e atributos que sua classe precisa, você não precisa redefini-la ou copiar esse código para ter o mesmo comportamento e

Page 3: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

atributos. Sua classe recebe automaticamente as características de sua superclasse. Essa superclasse também recebe da sua superclasse e assim por diante, formando uma cadeia até o inicio da hierarquia. Sua classe é, portanto, uma combinação de todos os recursos das classes que estão acima dela na hierarquia, assim como de seus próprios recursos.

Em Java, indicamos que uma classe herda características de outra através da palavra reservada extends. O código abaixo ilustra isso:

Voltemos ao exemplo da classe Carro. Poderíamos criar um projeto mais completo, com uma classe mais geral chamada Veículo. A figura abaixo ilustra como poderíamos estruturar o projeto.

Na classe Veículo definiríamos apenas características ou operações comuns a qualquer subclasse, tais como: cor, marca, etc. Abaixo da classe Veículo, as subclasses Carro e Caminhão teriam atributos e operações específicos de cada uma.

public class Animal {

...

}

public class Mamifero extends Animal {

...

}

Page 4: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Utilizando a herança é possível definir uma característica ou uma operação apenas uma vez na hierarquia. A partir daí ela é reutilizada automaticamente por cada subclasse.

Quando se cria uma nova instância de uma classe, é criado um espaço para cada atributo definido na classe corrente e nas superclasses.

Os métodos funcionam de modo semelhante: os novos objetos têm acesso a todos os nomes de métodos de sua classe e de suas superclasses. Isso é determinado dinamicamente, quando um método é utilizado em um programa que está em execução. Ao chamar um método de um objeto em particular, o interpretador Java procurará primeiramente na classe do objeto por esse método. Se o método não for encontrado, o interpretador procurará na superclasse dessa classe e assim por diante, até que a definição do método seja encontrada.

Suponhamos, por exemplo, que tenha sido declarada a seguinte classe:

Agora suponhamos a criação uma classe que tenha o CREA de um Engenheiro, além do seu CPF. Basta fazer:

public class Profissional {

private String cpf;

protected float salario;

public String getCPF(){

return this.cpf;

}

public void setCPF(String cpf){

this.cpf=cpf;

}

public float getSalario(){

return this.salario;

}

public void setSalario(float salario){

this.salario=salario;

}

}

Page 5: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

A classe Engenheiro é uma classe derivada da classe Profissional, da qual herda todos os dados e os métodos nela contidos.

A acessibilidade na estrutura de herança: Como definido na UML, os atributos e métodos private só podem ser

acessados internamente pelo objeto da classe proprietária.

Como definido na UML, os atributos e métodos protected podem ser

acessados internamente pelos objetos da classe proprietária ou pelo

objetos das classes filhas. Contudo, em Java, os objetos das classes que

estão no mesmo pacote também podem acessar atributos e métodos

protected.

Como definido na UML, os atributos e métodos públicos podem ser

acessados por qualquer objetos externo.

Todos os membros de superclasse public e protected mantêm seu

modificador de acesso quando se tornam membros da subclasse.

Seja, por exemplo, as classes Engenheiro e Profissional.

Um método da classe Engenheiro não pode acessar o atributo cpf

diretamente. O único modo é através dos métodos get e set que são

públicos. Após instanciado um objeto da classe engenheiro, é possível

public class Engenheiro extends Profissional{

private String crea;

private float pisoSalarial;

public String getCREA(){

return this.crea;

}

public void setCREA(String crea){

this.crea=crea;

}

public float getPisoSalarial(){

return this.pisoSalarial;

}

public void setPisoSalarial(float pisoSalarial){

this.pisoSalarial=pisoSalarial;

}

}

Page 6: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

invocar os métodos public, inclusive getCPF() e setCPF() – princípio da

herança.

Um método da classe Engenheiro pode acessar o atributo salario

diretamente (de acordo com a UML e o Java). Métodos de (objetos)

outras classes dentro do mesmo pacote também podem acessar o

atributo salario (de acordo com o Java);

Polimorfismo Até o momento, todas as operações de uma classe eram descritas por um único método. Ocorre que a implementação de serviços de uma classe pode variar de diversas formas: desde a passagem de um parâmetro para complemento da operação até a mudança efetiva do modo de execução.

Suponha o exemplo das classes Profissional e Engenheiro. Suponha, ainda, que o salário de um profissional possa sofrer um desconto percentual diferenciado. Uma possível solução seria criar um parâmetro no método getSalario(float desconto) para explicitar o desconto. Se passado o valor 0 (zero), então nenhum desconto é aplicado.

Uma outra possível solução é preservar o método getSalario() atual para salários sem desconto e criar um novo método diferente do já existente com um nome diferente: getSalarioComDesconto (float desconto)

Estas soluções, por mais funcionais que sejam, não são elegantes sob o ponto de vista da Orientação a Objetos.

Podemos definir um método com mesma assinatura, mas com número e tipo de parâmetros diferentes: um método getSalario() sem parâmetros que retorna o salário sem desconto nenhum e um outo método getSalario(float desconto) que retorna o salário com base no desconto passado. Criar métodos com nomes iguais e que se diferenciam pelos parâmetros (tipo e quantidade) se chama polimorfismo de sobrecarga (@overload).

Page 7: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Agora, suponha que o salário de um engenheiro deva respeitar o piso nacional. Uma possível solução é criar um método na classe Engenheiro que verifique se o salário respeita o piso. Podemos chamá-lo de setSalarioPiso(float salario). No entanto, pelo princípio da herança, o método setSalario(float salario) ainda pode ser invocado externamente, o que poderia gerar uma inconsistência nos dados do objeto (um engenheiro poderia ganhar menos que o piso).

Esta solução não é só deselegante, mas também traz problemas com a lógica do sistema.

Podemos definir um método com mesma assinatura, com número e tipo de parâmetros iguais na classe Engenheiro: um método setSalario(float salario) que verifica, internamente, se o salário respeita o piso de engenheiro. Ao definir um método de mesmo nome na classe filha, o método da classe filha sobrescreve o método da classe mãe. Criar métodos com nomes iguais com os mesmos parâmetros (tipo e quantidade) se chama polimorfismo de sobrescrita (@override).

public class Profissional {

private String cpf;

protected float salario;

public String getCPF(){

return this.cpf;

}

public void setCPF(String cpf){

this.cpf=cpf;

}

public float getSalario(){

return this.salario;

}

@overload

public float getSalario(float desconto){

return salario * (1-desconto);

}

public void setSalario(float salario){

this.salario=salario;

}

}

Page 8: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Uso do operador super No exemplo das classes Profissional e Engenheiro, o atributo salário tem nível de acessibilidade protected. Isso permitiu que o método sobrescrito setSalario trabalhasse a lógica em cima do atributo. Isso não teria nenhum problema se o Java fosse aderente a UML com relação ao nível protected. Ocorre que, no Java, qualquer objeto de classes do mesmo pacote poderão fazer alterações diretas no atributo salário.

Então, o correto é transformar o atributo salário em private, certo? Sim. No entanto, isso trará uma limitação, pois só poderemos modificar e acessar o atributo salario através de seu set e get. Para a acessar o método não há problema, pois o método get não foi sobrescrito. No entanto, o método set foi sobrescrito.

Os métodos de subclasse podem referir-se a membros public e protected herdados da superclasse simplesmente utilizando os nomes dos membros. Quando um método de subclasse sobrescrever um método de superclasse, o método da superclasse pode ser acessado a partir da subclasse precedendo o

public class Engenheiro extends Profissional{

private String crea;

private float pisoSalarial;

public String getCREA(){

return this.crea;

}

public void setCREA(String crea){

this.crea=crea;

}

public float getPisoSalarial(){

return this.pisoSalarial;

}

public void setPisoSalarial(float pisoSalarial){

this.pisoSalarial=pisoSalarial;

}

@override

public void setSalario(float salario){

if (salario < pisoSalarial)

this.salario = pisoSalarial;

else

this.salario = salario;

}

}

Page 9: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

nome do método da superclasse com a palavra-chave super e o separador (.).

public class Profissional {

private String cpf;

private float salario;

public String getCPF(){

return this.cpf;

}

public void setCPF(String cpf){

this.cpf=cpf;

}

public float getSalario(){

return this.salario;

}

@overload

public float getSalario(float desconto){

return salario * (1-desconto);

}

public void setSalario(float salario){

this.salario=salario;

}

}

public class Engenheiro extends Profissional{

private String crea;

private float pisoSalarial;

public String getCREA(){

return this.crea;

}

public void setCREA(String crea){

this.crea=crea;

}

public float getPisoSalarial(){

return this.pisoSalarial;

}

public void setPisoSalarial(float pisoSalarial){

this.pisoSalarial=pisoSalarial;

}

@override

public setSalario(float salario){

if (salario < pisoSalarial)

super.setSalario(pisoSalarial);

else

super.setSalario(salario);

}

}

Page 10: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Construtores polimórficos

Como vimos anteriormente, um construtor é um método que é invocado por padrão quando criamos um objeto. Vimos, também, que podemos criar um construtor e colocar a lógica de inicialização dentro dele.

Tal qual um método, um construtor pode ter diferentes implementações e possuir sobrecarga e sobrescrita. A lógica de funcionamento é a mesma.

Vejamos a classe Profissional. Os atributos cpf e salário precisam ser inicializados através de um construtor. No exemplo abaixo, os valores de inicialização são atribuídos incondicionalmente durante a chamada do construtor.

public Profissional () {

this.cpf = " . . . - ";

this.salario=0.0;

}

No entanto, podemos criar um construtor sobrecarregado que receba como argumento o valor do salário no construtor:

public Profissional (float salario) {

this.cpf = " . . . - ";

this.salario=salario;

}

Para fazer referência aos construtores já carregados, podemos simplesmente referenciá-los através do operador this:

public Profissional () {

this(0.0); //Invoca Profissional (float salario)

}

Agora, vamos dar uma olhada na classe Engenheiro. Ao criar um construtor para esta classe sem nenhum parâmetro, estamos sobrescrevendo o construtor da classe mãe.

Page 11: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

public Engenheiro () {

this.pisoSalarial = 7000;

this.salario = pisoSalarial;

setCpf ( " . . . - ");

}

Em função da herança, perceba que existe uma repetição de código desnecessária. Podemos, através do operador super, invocar construtores da classe mãe.

public Engenheiro () {

super(7000);

this.pisoSalarial = 7000;

}

O diagrama de classes das duas classes ficou assim:

Page 12: Notas de Aula 04: Herança e polimorfismo. · 2019-08-17 · diretamente (de acordo com a UML e o Java). Métodos de (objetos) outras classes dentro do mesmo pacote também podem

Disciplina: Linguagem de Programação

Professores: José Gomes de Carvalho Júnior, D.Sc. Pablo Rangel, D.Sc.

Exercícios

1) Crie um projeto no NetBeans com as classes Profissional e Engenheiro com seus respectivos atributos e métodos.

2) Crie mais uma classe chamada Médico que herde da classe Profissional. Um médico tem um CRM e uma especialidade médica. Os atributos devem ser privados. Crie os getters e setters. Crie um construtor que receba todos os valores de todos os atributos.

3) Na classe Profissional, sobrescreva o método toString() (classe Object) que mostra na tela o cpf e o salário do profissional. Nas classes filhas, sobrescreva este método e mostre na tela os dados pertinentes daquela classe além dos dados da classe mãe (dica: utilize o super).

4) Crie uma classe Principal que instancie dois engenheiros e dois médicos. O salário de um dos engenheiros deve ser menor do que o piso. Imprima os dados dos quatro profissionais.