class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Essas classes internas são chamadas de aninhadas. Eles são divididos em 2 tipos:
- Classes aninhadas não estáticas. Essas também são chamadas de classes internas.
- Classes aninhadas estáticas.
- uma classe local
- uma aula anônima
public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
Aqui temos a Bicycle
aula. Possui 2 campos e 1 método: start()
. Difere de uma classe comum porque contém duas classes: Handlebar
e Seat
. Seu código é escrito dentro da Bicycle
classe. Essas são classes completas: como você pode ver, cada uma delas tem seus próprios métodos. Neste ponto, você pode ter uma pergunta: por que diabos colocaríamos uma classe dentro da outra? Por que torná-los classes internas? Bem, suponha que precisamos de classes separadas para os conceitos de guidão e assento em nosso programa. Claro, não é necessário para nós torná-los aninhados! Podemos fazer aulas normais. Por exemplo, assim:
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
Muito boa pergunta! Claro, não estamos limitados pela tecnologia. Fazer isso é certamente uma opção. Aqui, o importante é mais o desenho correto das aulas do ponto de vista de um programa específico e sua finalidade. As classes internas são para separar uma entidade que está inextricavelmente conectada a outra entidade. Guidão, assentos e pedais são componentes de uma bicicleta. Separados da bicicleta, eles não fazem muito sentido. Se fizéssemos todos esses conceitos de classes públicas separadas, teríamos o código assim em nosso programa:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
Hmm... O significado desse código é até difícil de explicar. Temos um guidão vago (por que é necessário? Não faço ideia, para ser honesto). E essa maçaneta vira para a direita... sozinha, sem uma bicicleta... por algum motivo. Ao separar o conceito de guidão do conceito de bicicleta, perdemos alguma lógica em nosso programa. Usando uma classe interna, o código parece muito diferente:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
Saída do console:
Seat up!
Let's go!
Steer left!
Steer right!
Agora o que vemos de repente faz sentido! :) Criamos um objeto bicicleta. Criamos dois "subobjetos" de bicicleta — um guidão e um assento. Levantamos o assento para maior conforto e lá fomos nós: pedalando e virando conforme a necessidade! :) Os métodos que precisamos são chamados nos objetos apropriados. É tudo simples e conveniente. Neste exemplo, separar o guidão e o assento melhora o encapsulamento (ocultamos os dados sobre as peças da bicicleta dentro da classe relevante) e nos permite criar uma abstração mais detalhada. Agora vamos ver uma situação diferente. Suponha que queremos criar um programa que simule uma loja de bicicletas e peças de reposição para bicicletas. Nesta situação, nossa solução anterior não funcionará. Em uma loja de bicicletas, cada peça individual da bicicleta faz sentido mesmo quando separada de uma bicicleta. Por exemplo, precisaremos de métodos como "vender pedais a um cliente", "comprar um novo assento", etc. Seria um erro usar classes internas aqui - cada peça individual de bicicleta em nosso novo programa tem um significado que depende próprio: pode ser separado do conceito de bicicleta. É exatamente nisso que você precisa prestar atenção se estiver se perguntando se deve usar classes internas ou organizar todas as entidades como classes separadas. A programação orientada a objetos é boa porque facilita a modelagem de entidades do mundo real. Esse pode ser seu princípio orientador ao decidir se deve usar classes internas. Em uma loja de verdade, as peças sobressalentes são separadas das bicicletas - tudo bem. Isso significa que também está tudo bem ao projetar um programa. Ok, descobrimos a "filosofia" :) Agora vamos nos familiarizar com importantes recursos "técnicos" das classes internas. Aqui está o que você definitivamente precisa lembrar e entender:
-
Um objeto de uma classe interna não pode existir sem um objeto de uma classe externa.
Isso faz sentido: é por isso que criamos as classes internas
Seat
eHandlebar
internas em nosso programa — para não acabarmos com guidões e assentos órfãos.Este código não compila:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
Outra característica importante decorre disso:
-
Um objeto de uma classe interna tem acesso às variáveis da classe externa.
Por exemplo, vamos adicionar uma
int seatPostDiameter
variável (representando o diâmetro do canote) à nossaBicycle
classe.Então, na
Seat
classe interna, podemos criar umdisplaySeatProperties()
método que exibe as propriedades do assento:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
E agora podemos exibir essas informações em nosso programa:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
Saída do console:
Seat properties: seatpost diameter = 40
Observação:a nova variável é declarada com o modificador de acesso mais estrito (
private
). E ainda a turma interna tem acesso! -
Um objeto de uma classe interna não pode ser criado em um método estático de uma classe externa.
Isso é explicado pelas características específicas de como as classes internas são organizadas. Uma classe interna pode ter construtores com parâmetros ou apenas o construtor padrão. Mas independentemente disso, quando criamos um objeto de uma classe interna, uma referência ao objeto da classe externa é passada de forma invisível para o objeto criado da classe interna. Afinal, a presença de tal referência de objeto é um requisito absoluto. Caso contrário, não poderemos criar objetos da classe interna.
Mas se um método da classe externa for estático, talvez não tenhamos um objeto da classe externa! E isso seria uma violação da lógica de funcionamento de uma classe interna. Nesta situação, o compilador irá gerar um erro:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Uma classe interna não pode conter variáveis e métodos estáticos.
A lógica é a mesma: métodos e variáveis estáticos podem existir e ser chamados ou referenciados mesmo na ausência de um objeto.
Mas sem um objeto da classe externa, não teremos acesso à classe interna.
Uma clara contradição! É por isso que variáveis e métodos estáticos não são permitidos em classes internas.
O compilador irá gerar um erro se você tentar criá-los:
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
Ao criar um objeto de uma classe interna, seu modificador de acesso é importante.
Uma classe interna pode ser marcada com os modificadores de acesso padrão:
public
,private
,protected
epackage private
.Por que isso importa?
Isso afeta onde podemos criar instâncias da classe interna em nosso programa.
Se nossa
Seat
classe for declarada comopublic
, podemos criarSeat
objetos em qualquer outra classe. O único requisito é que um objeto da classe externa também exista.A propósito, já fizemos isso aqui:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
Obtivemos facilmente acesso à
Handlebar
classe interna daMain
classe.Se declararmos a classe interna como
private
, poderemos criar objetos apenas dentro da classe externa.Não podemos mais criar um
Seat
objeto "do lado de fora":private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
Você provavelmente já entendeu a lógica :)
-
Modificadores de acesso para classes internas funcionam da mesma forma que para variáveis comuns.
O
protected
modificador fornece acesso a uma variável de instância em subclasses e classes que estão no mesmo pacote.protected
também funciona para classes internas. Podemos criarprotected
objetos da classe interna:- na classe externa;
- em suas subclasses;
- em classes que estão no mesmo pacote.
Se a classe interna não tiver um modificador de acesso (
package private
), os objetos da classe interna podem ser criados:- na classe externa;
- em classes que estão no mesmo pacote.
Você está familiarizado com os modificadores há muito tempo, então não há problemas aqui.
GO TO FULL VERSION