Herança Herança O que é herança? Herdar é derivar características de gerações precedentes. No mundo da Programação Orientada a Objetos, o termo é associado com uma das formas de reutilização de software. Através da herança, novas classes podem ser derivadas das classes existentes. A nova classe herda propriedades e métodos da classe base. A nova classe também pode adicionar suas próprias propriedades e métodos Herança Para que serve a herança? Considere a criação de uma classe ClasseB em Java. Que métodos estão disponíveis através de uma referência para a ClasseB (isto é, um objeto)? Herança Herança Suponha agora que a classe ClasseB herda de ClasseA Que métodos estão agora disponíveis para uma referência da ClasseB (um objeto) ? Herança Herança Poderoso mecanismo para o reaproveitamento de código O objeto objB tem agora disponíveis os métodos da ClasseA sem ser necessário reescrevê-los na ClasseB Um objeto da ClasseB também é um objeto da ClasseA. Facilita a manutenção do código: Os métodos não são replicados. Se for necessário alterar o código do método m3, basta alterá-lo em ClasseA. ClasseB pode "recusar" parte da herança reimplementando os métodos herdados Herança O método m3 agora disponível para objB é aquele implementado em ClasseB Herança Várias subclasses podem herdar da mesma superclasse Herança Em Java, não é permitido herdar de mais de uma classe ERRADO!!!! Herança A hierarquia de herança pode ter vários níveis Herança Que métodos estão agora disponíveis para uma instância de ClasseC? Herança A Herança também pode surgir a partir da refatoração de classes existentes de modo a eliminar a duplicação de código Grande quantidade de atributos e métodos duplicados... Herança Refatoração Superclasse (características comuns) Subclasses (características específicas) Herança O mecanismo de herança pode ser melhor entendido através do seguinte exemplo: Herança A classe ContaBancaria tem quatro atributos: nome: armazena o nome do cliente cpf: armazena o número do CPF do cliente numeroConta: armazena o número da conta do cliente saldo: armazena o saldo da conta Os métodos depositar e sacar são usados para fazer um depósito ou retirada da conta bancária. A classe ContaBancária, sozinha, não é suficiente para realizar todas as transações bancárias. Existem geralmente dois tipos de contas: a conta corrente e a conta de investimentos Herança Vamos derivar portanto duas subclasses que herdam da superclasse ContaCorrente Herança As subclasses ContaInvestimento e ContaCorrente herdam os atributos e métodos da classe ContaBancaria Herança Agora é possível fazer: Apesar do método depositar não ter sido definido para a classe ContaCorrente, ele está disponível devido ao mecanismo de herança Composição Composição A classe contém referências para objetos de outras classes Estas referências são também atributos da classe Uma maneira alternativa de estender a funcionalidade de uma classe agregando funcionalidades de outras classes Herança É UM vs. vs. Composição TEM UM Composição O mecanismo de herança nem sempre é apropriado Java não tem herança múltipla Estender funcionalidade através de herança pode não ser "natural": Uma conta de investimento é uma conta bancária Uma conta de investimento não é uma classe DecimalFormat! Composição Considere a seguinte situação: ::Project2 CA m1(...) m2(...) CB m3(...) m4(...) Composição Considere agora que é preciso estender a funcionalidade da classe CA oferecendo em sua interface também os métodos m3( ) e m4( ) Composição 1a solução: Implementar os métodos m3( ) e m4( ) na classe CA. ::Project2 CA m1(...) m2(...) m3(...) m4(...) CB m3(...) m4(...) Desvantagem: duplicação de métodos dificulta a manutenção do software Composição 2a solução: Herança Desvantagens: Conceitualmente, a classe CA pode não SER uma classe CB Java não suporta herança múltipla. E se fosse necessário oferecer os serviços m5() e m6() de uma classe CC? ::Project2 CB m3(...) m4(...) CA m1(...) m2(...) Composição 3a solução: Composição Composição Que serviços estão disponíveis para uma referência da classe CA? Composição No entanto, é possível fazer: Observe a utilização dos métodos m3 e m4 através de uma referência para a classe CB Composição Esse processo é também conhecido por delegação: a classe CA delega à classe CB a execução dos serviços m3 e m4 Observe que o atributo b na classe A é público. Diz-se então que a delegação é pública, isto é, ela é visível para os clientes da classe Um cliente da classe CA, para usar os métodos m3 e m4, deve estar ciente da delegação e fazer: obj.b.m3(); Composição É possível tornar a delegação privada: Composição Oferecer os serviços m3 e m4 na classe CA não significa, neste caso, duplicar código. Os métodos m3 e m4 em CA são apenas uma fachada para os métodos m3 e m4 em CB Composição Que serviços estão agora disponíveis para uma referência da classe CA? Observe que, agora, o cliente da classe não conhece a delegação: para ele tudo se passa como se os métodos m3 e m4 fossem implementados em CA Composição Um exemplo no próprio Delphi: A classe System Para imprimir uma String no console faz-se: System.out.println("Alo Mundo!"); método da classe PrintStream delegação Composição Composição Um serviço oferecido por uma classe pode não ser exatamente uma fachada para a classe delegada, mas uma combinação de serviços oferecidos por esta. Observe atentamente o código a seguir: public class Data { private int dia; private int mes; private int ano; public Data(int d, int m, int a) dia = d; mes = m; ano = a; } { public Data(String strData) { dia = Integer.parseInt(strData.substring(0, 2)); mes = Integer.parseInt(strData.substring(3, 5)); ano = Integer.parseInt(strData.substring(6)); } df.format() se public String toString() { o campo tem apenas um dígito preenche com // imprime a data no formato dd/mm/aaaa um zero à esquerda DecimalFormat df = new DecimalFormat("00"); return df.format(dia) + "/" + df.format(mes) + "/" + ano; } } monta uma String no formato dd/mm/aaaa public class Empregado { private Data admissao; private Data nascimento; private String nome; public Empregado(Data admissao, Data nascimento, String nome) { this.admissao = admissao; this.nascimento = nascimento; this.nome = nome; monta uma String } contendo o nome, data de nascimento e data de admissão do Empregado public String toString() { return "nome: " + nome + "\nnascimento: " + nascimento.toString() + "\nadmissao: " + admissao.toString(); } } public class Teste { public static void main(String args[]) { String aux = EasyIn.getString("Data do aniversario[dd/mm/aaaa]: "); Data data1 = new Data(aux); lê uma String contendo a data de aniversário e usa aux = EasyIn.getString("Data da admissao[dd/mm/aaaa]: "); o método substring para Data data2 = new Data(aux); separar o dia, o mês e o ano aux = EasyIn.getString("nome: "); Empregado emp = new Empregado(data2, data1, aux); System.out.println(emp.toString()); } o mesmo para a data de admissão } instancia um objeto Empregado invoca o método toString da classe Empregado. Este, por sua vez, usa o método toString da classe Data Data do aniversario[dd/mm/aaaa]: 09/03/1974 Data da admissao[dd/mm/aaaa]: 20/04/1994 nome: Rafael Marques nome: Rafael Marques nascimento: 09/03/1974 admissao: 20/04/1994 Process finished with exit code 0 Composição Vamos repetir o mesmo exemplo, dessa vez usando uma interface gráfica com o usuário Observe a utilização das mesmas classes de negócio (Empregado e Data) class Data private private private { int dia; int mes; int ano; public Data(int d, int m, int a) dia = d; mes = m; ano = a; } { public Data(String strData) { dia = Integer.parseInt(strData.substring(0, 2)); mes = Integer.parseInt(strData.substring(3, 5)); ano = Integer.parseInt(strData.substring(6)); } public String toString() { // imprime a data no formato dd/mm/aaaa DecimalFormat df = new DecimalFormat("00"); return df.format(dia) + "/" + df.format(mes) + "/" + ano; } } class Empregado { private Data admissao; private Data nascimento; private String nome; public Empregado(Data admissao, Data nascimento, String nome) { this.admissao = admissao; this.nascimento = nascimento; this.nome = nome; } public String toString() { return "nome: " + nome + "\nnascimento: " + nascimento.toString() + "\nadmissao: " + admissao.toString(); } } public class Form extends JFrame implements ActionListener JTextField edtNasc, edtAdm, edtNome; public Form() { super("Composição"); Container c = getContentPane(); c.setLayout(new FlowLayout()); c.add(new JLabel("Nome:")); edtNome = new JTextField(20); c.add(edtNome); c.add(new JLabel("Nascimento:")); edtNasc = new JTextField(10); c.add(edtNasc); c.add(new JLabel("Admissão:")); edtAdm = new JTextField(10); c.add(edtAdm); JButton btnImprime = new JButton("Imprime"); c.add(btnImprime); btnImprime.addActionListener(this); this.setSize(400, 200); this.setVisible(true); } { public static void main(String args[]) new Form(); } { public void actionPerformed(ActionEvent e) { Data data1 = new Data(edtNasc.getText()); Data data2 = new Data(edtAdm.getText()); Empregado emp = new Empregado(data2, data1, edtNome.getText()); JOptionPane.showMessageDialog(null, emp.toString()); } }