POO – AULA 03 Objetivos da aula Introduzir o conceito de herança Criar uma hierarquia de classes Detalhar o funcionamento da herança Rever o uso dos modificadores de acesso 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 MOO 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 classefilha 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). A seguir uma figura ilustrativa do conceito de herança. 1 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 e 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 { ... } 2 Voltemos ao exemplo da classe Carro. Poderíamos criar um projeto mais completo, com uma classe mais geral chamada Veículo. A figura ao lado 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 sobre acessibilidade na estrutura de herança: 3 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. 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 (.). Para observarmos um relacionamento entre superclasse e subclasse, 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 EmpComissionado.java. nome da classe, portanto, //Classe Empregado Comissionado public class EmpComissionado extends Object { protected String nome; protected double totalVendas; protected double taxaComissao; // construtor com 3 argumentos public EmpComissionado (String n,double vendas, double comissao){ nome = n; setTotalVendas (vendas); setTaxaComissao (comissao); } public void setNome (String n){ nome = n; } public String getNome() { return nome; } public void setTotalVendas (double vendas){ totalVendas = (vendas < 0.0) ? 0.0 : vendas; } public double getTotalVendas () { return totalVendas; 4 } public void setTaxaComissao (double tx){ taxaComissao = (tx < 0.0) ? 0.0 : tx; } public double getTaxaComissao () { return taxaComissao; } public double calcularSalario(){ // Calcula o salário return taxaComissao * totalVendas; } public void mostrarAtributos () { System.out.println("Nome do empregado: " + nome); System.out.println("Total de Vendas: " + String.format("%.2f",totalVendas)); System.out.println("Taxa de Comissão: "+ String.format("%.2f",taxaComissao)); System.out.println("Salario Total: " + String.format("%.2f",calcularSalario())); } } // 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, double vendas, double comissao, double salBase){ // chama construtor da superclasse super (n, 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; 5 } public double calcularSalario(){ // Calcula o salário return salarioBase + super.calcularSalario(); } // Mostra as informações public void mostrarAtributos () { super.mostrarAtributos(); System.out.println("Salario Base: " + String.format("%.2f", salarioBase)); } } // 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", 2000.00, 0.1, 300); // obtém os dados do empregado System.out.println ("Dados do Empregado:"); System.out.printf ("%s %s\n", "Nome: ", empregado.getNome() ); 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.calcularSalario() ); System.out.printf ("%s %.2f\n\n", "Salario base:", empregado.getSalarioBase() ); System.out.println ("**Informacoes do empregado" + " via o método mostrarAtributos**"); empregado.mostrarAtributos(); } // fim do método main } // fim da classe Principal 6 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. 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 nos métodos que julgar necessárias para realizar o novo cálculo. 7