Olá! Hoje veremos a operação de um mecanismo importante - herança em classes aninhadas. Não sei se você já pensou no que fará quando precisar herdar uma classe aninhada de outra. Se não, acredite: esta situação pode ser confusa, porque há muitas nuances aqui:
- Herdamos uma classe aninhada de alguma classe ou herdamos outra classe de uma classe aninhada?
- O sucessor/herdado é uma classe pública regular ou também é uma classe aninhada?
- Finalmente, que tipo exato de classes aninhadas estamos usando em todas essas situações?
Classes aninhadas estáticas
Suas regras de herança são as mais simples. Aqui você pode fazer quase tudo que seu coração desejar. Uma classe aninhada estática pode ser herdada de:- aula normal
- uma classe estática aninhada que é declarada na classe externa ou em seus ancestrais
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Vamos tentar alterar o código e criar uma classe estática aninhada Drawing
e seu descendente - Boeing737Drawing
.
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Como você pode ver, não há problema. Podemos remover completamente a classe Drawing
e torná-la uma classe pública regular em vez de uma classe estática aninhada - nada mudará.
public class Drawing {
}
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Boeing737Drawing extends Drawing {
public static int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Isso está resolvido. E quais classes podem herdar de uma classe aninhada estática? Quase qualquer um! Aninhado/regular, estático/não estático - não importa. Aqui, herdamos a classe interna Boeing737Drawing
da classe aninhada estática Drawing
:
public class Boeing737 {
private int manufactureYear;
private static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
public class Boeing737Drawing extends Drawing {
public int getMaxPassengersCount() {
return maxPassengersCount;
}
}
}
Você pode criar uma instância Boeing737Drawing
como esta:
public class Main {
public static void main(String[] args) {
Boeing737 boeing737 = new Boeing737(1990);
Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
System.out.println(drawing.getMaxPassengersCount());
}
}
Embora nossa classe Boeing737Drawing
herde de uma classe estática, ela mesma não é estática! Portanto sempre precisará de uma instância da classe externa. Podemos tirar a classe Boeing737Drawing
da classe Boeing737
e torná-la apenas uma classe pública. Nada mudará - ele também pode herdar de um aninhado estático Drawing
.
public class Boeing737 {
private int manufactureYear;
public static int maxPassengersCount = 300;
public Boeing737(int manufactureYear) {
this.manufactureYear = manufactureYear;
}
public int getManufactureYear() {
return manufactureYear;
}
public static class Drawing {
}
}
public class Boeing737Drawing extends Boeing737.Drawing {
public int getMaxPassengersCount() {
return Boeing737.maxPassengersCount;
}
O único ponto importante: neste caso precisamos tornar maxPassengersCount
pública a variável estática. Se permanecer privado, a classe pública normal não terá acesso a ele. Classificamos as classes estáticas! :) Agora vamos passar para as aulas internas. Como você se lembra, existem 3 tipos: classes internas simplesmente, classes locais e classes internas anônimas. Mais uma vez, vamos passar do simples ao complexo :)
Classes internas anônimas
Uma classe interna anônima não pode herdar de outra classe. Nenhuma outra classe pode herdar de uma classe anônima. Não poderia ser mais simples! :)Aulas locais
Classes locais (caso você tenha esquecido) são declaradas dentro de um bloco de código de outra classe. Na maioria das vezes - dentro de algum método desta classe externa. É lógico que apenas outras classes locais dentro do mesmo método (ou bloco) possam herdar de uma classe local. Aqui está um exemplo:public class PhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber() {
this.phoneNumber = number;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
class CellPhoneNumber extends PhoneNumber {
}
class LandlinePhoneNumber extends PhoneNumber {
}
//...code валидации номера
}
}
Este é o código da nossa palestra sobre aulas locais. Dentro da classe validadora de número temos uma classe local PhoneNumber
- número de telefone. Se para os nossos propósitos precisarmos separar dele duas entidades distintas, por exemplo, um número de telemóvel e um número de telefone fixo, só o poderemos fazer dentro do mesmo método. O motivo é simples: o escopo de uma classe local está dentro do método (bloco) onde ela é declarada. Portanto, não poderemos usá-lo externamente de alguma forma (inclusive para herança). Contudo, a própria classe local tem possibilidades mais amplas de herança! Uma classe local pode herdar de:
- Aula normal.
- Uma classe interna declarada na mesma classe que a classe local ou em seus ancestrais.
- De outra classe local declarada no mesmo método (bloco).
public class PhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
//...code валидации номера
}
}
Aqui retiramos a classe PhoneNumber
do método validatePhoneNumber()
e a tornamos interna em vez de local. Isso não nos impede de herdar dele nossas 2 classes locais. Exemplo 2 – “...ou nos ancestrais desta classe.” É aqui que fica mais interessante. Podemos PhoneNumber
subir ainda mais na cadeia de herança. Vamos declarar uma classe abstrata AbstractPhoneNumberValidator
que se tornará nossa ancestral PhoneNumberValidator
:
public abstract class AbstractPhoneNumberValidator {
class PhoneNumber {
private String phoneNumber;
public PhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
}
}
Como você pode ver, não apenas o declaramos, mas também movemos a classe interna para ele PhoneNumber
. No entanto, em sua classe descendente - PhoneNumberValidator
- classes locais em métodos podem herdar de PhoneNumber
!
public class PhoneNumberValidator extends AbstractPhoneNumberValidator {
public void validatePhoneNumber(final String number) {
class CellPhoneNumber extends PhoneNumber {
public CellPhoneNumber(String phoneNumber) {
super(number);
}
}
class LandlinePhoneNumber extends PhoneNumber {
public LandlinePhoneNumber(String phoneNumber) {
super(number);
}
}
//...code валидации номера
}
}
Graças à conexão por herança, as classes locais dentro de uma classe descendente “vêem” as classes internas dentro do ancestral. E por fim, vamos passar para o último grupo :)
Aulas internas
Uma classe interna pode ser herdada por outra classe interna declarada na mesma classe externa (ou sua descendente). Vejamos isso usando nosso exemplo de bicicleta da palestra sobre classes internas.public class Bicycle {
private String model;
private int mawWeight;
public Bicycle(String model, int mawWeight) {
this.model = model;
this.mawWeight = mawWeight;
}
public void start() {
System.out.println("Go!");
}
class Seat {
public void up() {
System.out.println("Сидение поднято выше!");
}
public void down() {
System.out.println("Сидение опущено ниже!");
}
}
class SportSeat extends Seat {
//...methods
}
}
Bicycle
Aqui declaramos uma classe interna dentro da classe Seat
- assento. Um subtipo especial de assentos de corrida foi herdado dele - SportSeat
. No entanto, poderíamos criar um tipo separado de “bicicletas de corrida” e colocá-lo em uma classe separada:
public class SportBicycle extends Bicycle {
public SportBicycle(String model, int mawWeight) {
super(model, mawWeight);
}
class SportSeat extends Seat {
public void up() {
System.out.println("Сидение поднято выше!");
}
public void down() {
System.out.println("Сидение опущено ниже!");
}
}
}
Isto também é possível. A classe interna do filho ( SportBicycle.SportSeat
) “vê” as classes internas do ancestral e pode herdar delas. A herança de classes internas tem uma característica muito importante! Nos dois exemplos anteriores SportSeat
tivemos internal. Mas e se decidirmos torná-la SportSeat
uma classe pública regular, que também herda da classe interna Seat
?
//ошибка! No inclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {
public SportSeat() {
}
public void up() {
System.out.println("Сидение поднято выше!");
}
public void down() {
System.out.println("Сидение опущено ниже!");
}
}
Tivemos um erro! Você consegue adivinhar com o que isso está conectado? :) É simples. Quando falamos sobre a classe interna Bicycle.Seat
, mencionamos que o construtor da classe interna passa implicitamente uma referência a um objeto da classe externa. Portanto, sem criar um objeto, Bicycle
você não pode criar um objeto Seat
. E a criação SportSeat
? Ele não possui o mesmo mecanismo interno para passar implicitamente uma referência a um objeto de classe externa no construtor como em Seat
. Porém, sem um objeto Bicycle
, assim como no caso de Seat
, não podemos criar um objeto SportSeat
. Portanto, só nos resta uma coisa a fazer - passar explicitamente SportSeat
uma referência ao objeto para o construtor. Bicycle
Veja como isso é feito:
class SportSeat extends Bicycle.Seat {
public SportSeat(Bicycle bicycle) {
bicycle.super();
}
public void up() {
System.out.println("Сидение поднято выше!");
}
public void down() {
System.out.println("Сидение опущено ниже!");
}
}
Para isso usamos uma palavra especial super();
Agora, se quisermos criar um objeto SportSeat
, nada nos impedirá de fazer isso:
public class Main {
public static void main(String[] args) {
Bicycle bicycle = new Bicycle("Peugeot", 120);
SportSeat peugeotSportSeat = new SportSeat(bicycle);
}
}
Ufa, a palestra acabou sendo bem grande :) Mas você aprendeu muitas coisas novas! Agora é a hora de resolver alguns problemas! :)
GO TO FULL VERSION