Baixe o app para aproveitar ainda mais
Prévia do material em texto
Aula 05: HERANÇA Objetivos Recordando... Nas aulas passadas criamos uma classe Carro, que apresentava alguns atributos (cor, marca, motorEstado) 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, a Metodologia OO utiliza 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 filhas estendem a sua utilização básica da classe pai para outras utilizações mais especializadas). Introduzir o conceito de herança Criar uma hierarquia de classes Detalhar o funcionamento da herança Rever o uso dos modificadores de acesso 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, Mamifero é 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 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: public class Animal { ... } public class Mamifero extends Animal { ... } Voltemos ao exemplo da classe Carro. Poderíamos criar um projeto mais completo, com uma classe mais geral chamada Veículo. A figura a seguir 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. 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: class Poligono{ int cx, cy; // coordenadas do centro do polígono } Agora suponhamos que pretendamos criar uma classe que tenha as dimensões do quadrado, além das coordenadas do centro. Basta fazer: class Quadrado extends Polígono { int lado; // Comprimento do lado do quadrado } A classe Quadrado é uma classe derivada da classe Polígono, da qual herda todos os dados e os métodos nela contidos. Um problema com herança é que uma subclasse pode herdar métodos que ela não necessita ou que não deveria ter. Mesmo quando um método é adequado a uma subclasse, essa subclasse precisa freqüentemente de uma versão personalizada de método. Nesses casos, a subclasse pode sobrescrever (redefinir) o método da superclasse com uma implementação mais adequada. Observações : 1. Todos os membros de superclasse public e protected mantêm seu modificador de acesso quando se tornam membros da subclasse. 2. Os métodos de subclasse podem referir-se a membros public e protected herdados da superclasse simplesmente utilizando os nomes dos membros. Uso do “super” Herança consiste em uma subclasse receber as características (atributos e métodos) da superclasse. No entanto, ela também pode ser acrescida de outros elementos que não constem da superclasse. Construtores não são herdados. O comando super sempre se refere a uma superclasse. Todo método construtor de uma subclasse precisa também chamar um construtor para os campos de dados da superclasse, caso existam campos de instância também na superclasse. Se a superclasse tiver um construtor então as subclasses precisam usar super para passar os parâmetros, a fim de utilizar este construtor da superclasse. Exemplo: public class Cliente () { private String nome; private int numConta; public Cliente(String n, int c) //Construtor da Classe Cliente { nome = n; numConta = c; } } public class ClienteOuro extends Cliente { private double limiteChequeEspecial; public ClienteOuro (String n, int c, double limite) { super(n, c ); //precisa ser o primeiro comando do método limiteChequeEspecial = limite; } } 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 nome do método da superclasse com a palavra-chave super e o separador (.). OBJETOS A PARTIR DE SUBCLASSES Quando instanciamos um objeto a partir de uma subclasse, ele recebe os membros definidos na superclasse. De qualquer modo, o modelo de acesso aos membros continua valendo: membros demarcados como private não podem ser acessados nas subclasses, membros public ou protected podemser acessados usando a palavra reservada super ( [argumentos do construtor]), a qual deve ser a primeira instrução do construtor da subclasse. Exemplo: Uma loja que vende livros e CDs precisa catalogar seus produtos indicando seu código, descrição, preço, autor, artista, número IBSN e gravadora. Fazendo uso do conceito de generalização, podemos criar uma superclasse Produto, que deverá apresentar código, descrição e preço. Criamos uma subclasse Livro, a qual deverá apresentar autor e ISBN. E a subclasse CompactDisc, com artista e gravadora. Podemos dizer que Livro e CompactDisc são subclasses de Produto, ou seja especializações dessa classe. Agora, vejamos como escrever um código que permita implementar esse sistema; public class Produto extends Object { private int codigo; private double preco; private String descricao; public Produto (int c, double p, String d) { codigo = c; preco = p; descricao = d; } public int getCodigo () { return codigo; } public double getPreco ( ) { return preco; } public String getDescricao ( ) { return descricao; } } public class Livro extends Produto { private String autor; private String isbn; public Livro(int cL, double pL, String dL, String a, String i){ // chama um construtor da superclasse. // a instrução super() deve ser a primeira linha do construtor. super(cL, pL, dL); // define os atributos da classe. autor = a; isbn = i; } public String getAutor ( ) { return autor; } public String getISBN () { return isbn; } } public class CompacticDisc extends Produto { private String artista; private String gravadora; public CompactDisc (int cD, double pD, String dD, String a, String g) { //chama um construtor da superclasse. // a instrução super() deve ser a primeira linha do construtor super(cD, pD, dD); // define os atributos da classe. gravadora = gravadora; artista = artista; } public String getArtista() { return artista; } public String getGravadora( ){ return gravadora; } } Apresentaremos outro exemplo para fixarmos o relacionamento entre superclasse e subclasse. E para isto faremos uma implementação e utilizaremos uma hierarquia de herança que contém tipos de empregados em um aplicativo de folha de pagamento. Na empresa, os empregados comissionados recebem apenas uma porcentagem (comissão) de suas vendas, enquanto que os empregados fixos comissionados recebem um salário base mais a comissão. Vamos inicialmente criar a superclasse EmpComissionado em um arquivo de mesmo nome da classe, portanto, EmpComissionado.java. //Classe Empregado Comissionado public class EmpComissionado extends Object { protected String nome; protected String sobrenome; protected String cpf; protected double totalVendas; protected double taxaComissao; // Construtor com 5 argumentos public EmpComissionado (String n, String sn, String cic, double vendas,double comissao){ nome = n; sobrenome = sn; cpf = cic; setTotalVendas (vendas); setTaxaComissao (comissao); } public void setNome (String n){ nome = n; } public String getNome() { return nome; } public void setSobrenome (String sn){ sobrenome = sn; } public String getSobrenome() { return sobrenome; } public void setCpf (String cic){ cpf = cic; } public String getCpf() { return cpf; } public void setTotalVendas (double vendas){ totalVendas = (vendas < 0.0) ? 0.0 : vendas; } public double getTotalVendas () { return totalVendas; } public void setTaxaComissao (double tx){ taxaComissao = (tx < 0.0) ? 0.0 : tx; } public double getTaxaComissao () { return taxaComissao; } public double salario(){ // Calcula o salário return taxaComissao * totalVendas; } public String toString () { return String.format ( "%s%s %s\n%s%s\n%s%.2f\n%s%.2f\n%s%.2f", "Identificacao do empregado: ", nome, sobrenome, "CPF: ", cpf, "Total de Vendas: ", totalVendas, "Taxa de Comissão: ", taxaComissao, "Salario Total: ", salario() ); } } // fim da classe Empregado Comissionado A seguir vamos, criar a subclasse EmpFixoComissionado, que herda todos os atributos e métodos da superclasse EmpComissionado. Observe que a subclasse chama alguns métodos da superclasse, a começar pelo próprio construtor desta (lembre-se de colocar o código abaixo em um outro arquivo com o mesmo nome da classe). //Classe Empregado com salário base mais Comissão public class EmpFixoComissionado extends EmpComissionado { private double salarioBase; // atributo exclusivo public EmpFixoComissionado ( //constructor da classe String n, String sn, String cic, double vendas, double comissao, double salBase){ // chama construtor da superclasse super (n, sn, cic, vendas, comissao); // chamada a método da própria classe setSalarioBase (salBase); } public void setSalarioBase (double salBase){ salarioBase = (salBase < 0.0) ? 0.0 : salBase; } public double getSalarioBase() { return salarioBase; } public double salario(){ // Calcula o salário return salarioBase + super.salario(); } // Retorna as informações public String toString (){ return String.format( "%s%s %s\n%s%s\n%s%.2f\n%s%.2f\n%s%.2f\n%s%.2f", "Identificacao: ", nome, sobrenome, "CPF: ", cpf,"Total de Vendas: ", totalVendas, "Taxa Comissão: ",taxaComissao, "Salario Base: ", salarioBase, "Salario Total: ", salario()); } } // fim da classe Empregado Fixo Comissionado Finalmente, vamos fazer uma aplicação que use as classes criadas (edite essa aplicação de teste em um terceiro arquivo, que deve se chamar Principal.java): // Testando a classe Empregado fixo com comissão public class Principal{ // Método main public static void main ( String argv[]) { // Instancia objeto da classe EmpFixoComissionado EmpFixoComissionado empregado = new EmpFixoComissionado ("Roberto", "Silveira", "123456789-10", 20000, 0.02, 300); // obtém os dados do empregado System.out.println ("Dados do empregado: \n"); System.out.printf ("%s %s\n", "Nome: ", empregado.getNome() ); System.out.printf ("%s %s\n", "Sobrenome: ", empregado.getSobrenome() ); System.out.printf ("%s %s\n", "CPF: ", empregado.getCpf() ); System.out.printf ("%s %.2f\n", "Vendas: ", empregado.getTotalVendas() ); System.out.printf ("%s %.2f\n", "Comissao: ", empregado.getTaxaComissao() ); System.out.printf ("%s %.2f\n", "Salario total: ", empregado.salario() ); System.out.printf ("%s %.2f\n", "Salario base: ", empregado.getSalarioBase() ); System.out.printf ("\n%s: \n\n%s\n", "As mesmas informacoes do empregado via toString", empregado.toString() ); } // fim do método main } // fim da classe Principal Desafios 1. Altere o moderador de acesso do método salario() da classe EmpComissionado de public para private e compile este arquivo. A seguir compile o arquivo com a classe EmpFixoComissionado.Explique o erro de compilação. Em seguida, retorne a acessibilidade do método salario() para public em EmpComissionado e altere para private a acessibilidade de salario() em EmpFixoComissionado. Observe o erro acusado na compilação decorrente do fato de que a sobrescrição não pode “diminuir” a acessibilidade. 2. Na classe de teste, crie um objeto da classe EmpComissionado e teste-o 3. Crie um atributo bonus que represente um percentual a ser acrescido ao salário. Este bonus se aplica a todos os empregados mas deve ser contado em dobro para os empregados comissionados. Faça as alterações necessárias nos métodos para realizar o novo cálculo. 4. Crie uma super classe FiguraGeometrica para as classes Quadrado e Circulo criadas em outras aulas. Crie toda a hierarquia de classes e defina onde colocar os atributos coordenadaCentroX e coordenadaCentroY e suas acessibilidades. Crie um construtor para a superclasse FiguraGeometrica que seja chamado pelos construtores das classes derivadas Quadrado e Circulo. A superclasse deve possuir métodos para cálculo de área e perímetro que simplesmente devolvam zero como resultado. Esses métodos devem ser sobrescritos nas classes derivadas, de forma a realizarem as contas corretamente, de acordo com o tipo de figura da classe derivada. Os atributos de cada classe devem ser iniciados através de passagem de parâmetros para os construtores das respectivas classes. Crie uma classe de teste.
Compartilhar