Baixe o app para aproveitar ainda mais
Prévia do material em texto
© ELFS 59 • Considere a seguinte classe: • A classe Veiculo representa um veículo qualquer. • E se for necessário trabalhar com tipos específicos de veículos, como caminhão, carro de passeio ou motocicleta? Novas classes! • Para construir as novas classes: tomar a classe Veiculo como base (pois, caminhão, carro de passeio ou motocicleta são veículos). • As novas classes serão subclasses da classe Veiculo. • A construção de classes tendo como base uma outra classe permite construir uma hierarquia de classes. • Numa hierarquia de classes, a classe mais geral (superclasse) serve de base para a construção de classes mais específicas (subclasses). Veiculo − double velocidade − double posicao + Veiculo() + void atualizarVelocidade() + void atualizarPosicao() • As subclasses herdam os campos e métodos (públicos ou protegidos) de sua superclasse, que por sua vez, pode ter herdado campos e métodos de outras superclasses. • Em Java, uma classe pode ter apenas uma superclasse imediata. Isto é conhecido como estrutura de herança simples. • Por exemplo: Caminhao não pode ser subclasse de Veiculo e também ser subclasse de uma outra classe MinhaFrota. © ELFS 60 Veiculo − double velocidade − double posicao + Veiculo() + void atualizarVelocidade() + void atualizarPosicao() Motocicleta − boolean pagaPedagio Carro − int numPassageiros + void atualizarPosicao() Caminhao − double capacidade − int numEixos + void alterarCarga() © ELFS 61 • Definição de classe tendo como base uma outra classe: • O modificador estabelece o nível de acesso à classe. Classes só podem ser definidas no nível public (acesso total) ou pacote (ausência do modificador). • A palavra-chave super pode ser utilizada em uma subclasse para acessar os campos e métodos da superclasse (se acessíveis). A classe Object • Todas as classes herdam, em última instância, da classe Object. A classe Object possui métodos que podem ser reutilizados por todas as classes de um programa, por exemplo, o método toString(). • Em Java, a declaração de uma classe implicitamente inclui extends Object. [<modificador>] class <nome_classe> [extends <nome_superclasse>] { [<declaração_atributos>] [<declaração_construtores>] [<declaração_métodos>] } class Carro extends Veiculo { private int numPassageiros; ... } © ELFS 62 Redefinição de métodos • Métodos herdados de uma superclasse podem ser redefinidos na subclasse. A redefinição é uma nova implementação para o método herdado, específica para a subclasse. • Lembrando: a combinação do nome de um método com sua lista de parâmetros é conhecida como assinatura do método. • Para a redefinição, o método da subclasse deve ter a mesma assinatura do método herdado da superclasse. O valor de retorno (embora não sendo parte da assinatura) também deve ser o mesmo. • O modificador de acesso pode mudar, mas o método na subclasse não pode ser menos acessível do que o método da superclasse. • Se os métodos herdados da superclasse forem definidos na subclasse com assinaturas diferentes haverá a sobrecarga e não a redefinição dos métodos. private (package) protected public © ELFS 63 Exemplo: Figura # int base # int altura + Figura(int, int) + double calcularArea() + void mostrar() Triangulo − boolean retangulo + Triangulo(int, int) + double calcularArea() Quadrado + Quadrado(int) + void mostrar(int) Retangulo + Retangulo(int, int) Figura é a superclasse. Retangulo e Triangulo são subclasses de Figura. Quadrado é subclasse de Retangulo (ou seja Retangulo é superclasse de Quadrado). A classe Retangulo herda os campos base e altura (porque são protegidos e não privados) da classe Figura. A classe Retangulo herda também os métodos calcularArea() e mostrar() da classe Figura (porque são públicos). Construtores não são herdados. Na classe Triangulo, o método calcularArea() é redefinido. Quais são os campos e métodos da classe Quadrado? © ELFS 64 Polimorfismo • Numa hierarquia de classes, uma variável de referência para um objeto da superclasse pode apontar também para um objeto da subclasse. • Como cada objeto tem sua própria forma (definida pelos campos e métodos da classe), essa variável ao referenciar objetos de diferentes classes, terá diferentes formas (polimorfismo). • Exemplo: public class Midia { protected int codigo; protected float preco; public String getDetalhes() { return codigo + ": " + preco; } } public class DVD extends Midia { protected String autor; public String getDetalhes() { return super.getDetalhes() + " - " + autor; } } Midia − int codigo − float preco +String getDetalhes() DVD # String autor +String getDetalhes() Exemplo: © ELFS 65 Midia m; m = new Midia(); m.getDetalhes(); // será chamado o método da superclasse m = new DVD(); m.getDetalhes(); // será chamado o método da subclasse O método a ser chamado é definido em tempo de execução (runtime). A isso dá-se o nome de ligação dinâmica de método. Daí vem o polimorfismo. Observe que quando a variável m aponta para o objeto DVD, a partir de m tem-se acesso aos campos e métodos (herdados) da superclasse (Midia). No entanto, para os métodos de Midia que foram redefinidos em DVD, o acesso é feito aos métodos da subclasse. ! " Stack Heap m Midia DVD " ! © ELFS 66 • Considere a seguinte hierarquia de classes: • Imagine que desejamos implementar um método calcularPreco() para as classes CD e DVD. Podemos incluir esse método na classe Midia para que seja herdado pelas classes CD e DVD? Sim! Usando a palavra-reservada this e o operador instanceof. • A palavra this, usada no corpo de um método, corresponde ao objeto que chamou o método. • O operador instanceof permite identificar a classe para a qual uma variável de referência aponta. • Para a hierarquia de classe acima, uma variável declarada como: Midia m; # pode apontar para um objeto da classe Midia ou para um objeto de uma das subclasses CD ou DVD. Midia CD DVD © ELFS 67 • Podemos implementar o método calcularPreco() na classe Midia como: public void calcularPreco() { if (this instanceof CD) preco = 15.00; else if (this instanceof DVD) preco = 30.00; else preco = 2.00; } public class TestaMidia { public static void main(String arg[]) { Midia m = new Midia(); m.calcularPreco(); ... m = new CD(); m.calcularPreco(); ... } } É claro que, mesmo sem polimorfismo, por exemplo: CD c = new CD(); c.calcularPreco(); o método calcularPreco() retornaria o valor correto, devido ao uso do operador instanceof. © ELFS 68 Construtores e Herança • Um construtor de uma subclasse pode chamar explicitamente um construtor de sua superclasse utilizando o método super(). Neste caso, a chamada ao método super() deve ser a primeira linha do construtor da subclasse. O construtor da superclasse a ser chamado é definido pelos parâmetros passados ao método super(). • Se no construtor da subclasse nenhuma chamada a this() (construtor da própria classe) ou a super() for feita, o compilador incluirá automaticamente uma chamada a super(), ou seja, ao construtor sem parâmetros da superclasse. Se a superclasse não tiver um construtor sem parâmetros, um erro será gerado pelo compilador. • Se na subclasse não existir um construtor, a chamada super() também será inserida no construtor padrão (inserido automaticamente pelo compilador). • Objetivo: garantir que um objetoda classe Object seja instanciado. © ELFS 69 Exemplo: public class Object { public Object() {} } public class Midia extends Object { private int codigo; private double preco; public Midia(int c, double p) { // chamada implicita a super() codigo = c; preco = p; } public Midia(int c) { this(c, 0.0); } } public class DVD extends Midia { private String autor; public DVD(String a) { super(10); autor = a; } } Lembrar que: this() corresponde a uma chamada ao construtor da própria classe. super() corresponde a uma chamada ao construtor da superclasse. © ELFS 70 Exemplo: public class Ponto { private int x, y; // coordenadas do ponto public Ponto() { x = 0; y = 0; } public Ponto(int a, int b) { setX(a); setY(b); } public void setX(int a) { x = ((a > 0)? a : 0); } public void setY(int b) { y = ((b > 0)? b : 0); } public int getX() { return x; } public int getY() { return y; } public String toString() { return "[" + x + ", " + y + "]"; } } Haverá uma chamada implícita a super(), ou seja, o construtor padrão da classe Object. Ponto.java Haverá uma chamada implícita a super(). O método toString() da classe Ponto redefine o método toString() herdado da classe Object. © ELFS 71 public class Circulo extends Ponto { private double raio; public Circulo() { raio = 0; } public Circulo(double r, int a, int b) { super(a,b); setRaio(r); } public void setRaio(double r) { raio = (r >= 0)? r : 0.0; } public double getRaio() { return raio; } public double area() { return Math.PI * Math.pow(raio,2); } public String toString() { return "Centro = [ " + getX() + ", " + getY() + "]; Raio = " + raio; } } Haverá uma chamada implícita ao construtor padrão da classe Ponto. Se a classe Ponto tivesse apenas o construtor com parâmetros, haveria um erro de compilação. Circulo.java O método toString() da classe Circulo redefine o método toString() herdado da classe Ponto. Saída: © ELFS 72 public class TestaHierarquia { public static void main(String args[]) { Ponto p; Circulo c; p = new Ponto(30,50); System.out.println("Ponto: " + p); c = new Circulo(2.5,120,90); System.out.println("Circulo: " + c); p = c; if (p instanceof Circulo) { System.out.prinln("p se refere a um circulo"); System.out.println("Circulo: " + p); System.out.println("Area = " + c.area()); } } } TestaHierarquia.java Ponto: [30, 50] Circulo: Centro = [ 120, 90]; Raio = 2.5 p se refere a um circulo Circulo: Centro = [ 120, 90]; Raio = 2.5 Area = 19.634954084936208 Notar que p.area() seria incorreto, pois p foi declarado como Ponto e, portanto, em tempo de compilação, p não tem acesso ao método area(). © ELFS 73 A palavra-chave final • Algumas vezes pode ser interessante que um elemento de um programa (classe, campo ou método) não possa ser modificado. Nestes casos, a palavra-chave final deve ser usada. • Classe final: uma classe especificada como final não pode ter subclasses. A classe String é final: garante-se que essa classe não será alterada. • Método final: um método definido como final não pode ser redefinido. Métodos são definidos como final quando sua implementação não puder ser alterada por alguma razão. Métodos declarados como static ou como private são implicitamente final. Todos os métodos de uma classe final são implicitamente final. • Campo final: um campo definido como final não pode ser modificado, ou seja, é uma constante. Uma vez atribuído um valor ao campo, este valor não pode ser alterado. Uma classe pode ter um campo final que não é inicializado na definição e sim em um construtor, mas uma vez que a atribuição seja feita, o valor não pode mudar.
Compartilhar