class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Estas clases internas se denominan anidadas. Se dividen en 2 tipos:
- Clases anidadas no estáticas. También se denominan clases internas.
- Clases estáticas anidadas.
- una clase local
- una clase 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!");
}
}
}
Aquí tenemos la Bicycle
clase. Tiene 2 campos y 1 método: start()
. Se diferencia de una clase ordinaria en que contiene dos clases: Handlebar
y Seat
. Su código está escrito dentro de la Bicycle
clase. Estas son clases completas: como puede ver, cada una de ellas tiene sus propios métodos. En este punto, es posible que tenga una pregunta: ¿por qué demonios pondríamos una clase dentro de otra? ¿Por qué hacerlos clases internas? Bueno, supongamos que necesitamos clases separadas para los conceptos de manillar y asiento en nuestro programa. ¡Por supuesto, no es necesario que los hagamos anidados! Podemos hacer clases ordinarias. Por ejemplo, así:
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!");
}
}
¡Muy buena pregunta! Por supuesto, no estamos limitados por la tecnología. Hacer eso es ciertamente una opción. Aquí, lo importante es más el diseño correcto de las clases desde la perspectiva de un programa específico y su propósito. Las clases internas son para separar una entidad que está indisolublemente conectada a otra entidad. Los manillares, los asientos y los pedales son componentes de una bicicleta. Separados de la bicicleta, no tienen mucho sentido. Si hiciéramos todos estos conceptos como clases públicas separadas, tendríamos un código como este en nuestro programa:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
Hmm... El significado de este código es incluso difícil de explicar. Tenemos un manillar vago (¿Por qué es necesario? Ni idea, para ser honesto). Y este mango gira a la derecha... por sí solo, sin bicicleta... por alguna razón. Al separar el concepto de manillar del concepto de bicicleta, perdimos algo de lógica en nuestro programa. Usando una clase interna, el código se ve muy 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();
}
}
Salida de la consola:
Seat up!
Let's go!
Steer left!
Steer right!
¡Ahora lo que vemos de repente tiene sentido! :) Creamos un objeto de bicicleta. Creamos dos "subobjetos" de bicicleta: un manillar y un asiento. Levantamos el asiento para mayor comodidad y nos fuimos: pedaleando y dirigiendo según sea necesario. :) Los métodos que necesitamos se llaman en los objetos apropiados. Todo es simple y conveniente. En este ejemplo, separar el manillar y el asiento mejora la encapsulación (ocultamos datos sobre las piezas de la bicicleta dentro de la clase correspondiente) y nos permite crear una abstracción más detallada. Ahora veamos una situación diferente. Supongamos que queremos crear un programa que simule una tienda de bicicletas y repuestos para bicicletas. En esta situación, nuestra solución anterior no funcionará. En una tienda de bicicletas, cada pieza individual de la bicicleta tiene sentido incluso cuando se separa de una bicicleta. Por ejemplo, necesitaremos métodos como "vender pedales a un cliente", "comprar un asiento nuevo", etc. Sería un error usar clases internas aquí: cada parte individual de la bicicleta en nuestro nuevo programa tiene un significado que se sostiene en propio: se puede separar del concepto de bicicleta. Esto es precisamente a lo que debe prestar atención si se pregunta si debe usar clases internas u organizar todas las entidades como clases separadas. La programación orientada a objetos es buena porque facilita el modelado de entidades del mundo real. Este puede ser su principio rector al decidir si usar clases internas. En una tienda real, las piezas de repuesto están separadas de las bicicletas, esto está bien. Esto significa que también está bien al diseñar un programa. Bien, hemos descubierto la "filosofía" :) Ahora vamos a familiarizarnos con importantes características "técnicas" de las clases internas. Esto es lo que definitivamente necesita recordar y comprender:
-
Un objeto de una clase interna no puede existir sin un objeto de una clase externa.
Esto tiene sentido: es por eso que creamos las clases internas
Seat
yHandlebar
en nuestro programa, para que no terminemos con manubrios y asientos huérfanos.Este código no compila:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
Otra característica importante se desprende de esto:
-
Un objeto de una clase interna tiene acceso a las variables de la clase externa.
Por ejemplo, agreguemos una
int seatPostDiameter
variable (que representa el diámetro de la tija del sillín) a nuestraBicycle
clase.Luego, en la
Seat
clase interna, podemos crear undisplaySeatProperties()
método que muestre las propiedades del asiento: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); } } }
Y ahora podemos mostrar esta información en nuestro 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(); } }
Salida de la consola:
Seat properties: seatpost diameter = 40
Nota:la nueva variable se declara con el modificador de acceso más estricto (
private
). ¡Y todavía la clase interna tiene acceso! -
Un objeto de una clase interna no se puede crear en un método estático de una clase externa.
Esto se explica por las características específicas de cómo se organizan las clases internas. Una clase interna puede tener constructores con parámetros, o simplemente el constructor predeterminado. Pero independientemente, cuando creamos un objeto de una clase interna, una referencia al objeto de la clase externa se pasa de manera invisible al objeto creado de la clase interna. Después de todo, la presencia de tal referencia de objeto es un requisito absoluto. De lo contrario, no podremos crear objetos de la clase interna.
Pero si un método de la clase externa es estático, ¡es posible que no tengamos un objeto de la clase externa! Y esto sería una violación de la lógica de cómo funciona una clase interna. En esta situación, el compilador generará un error:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
Una clase interna no puede contener variables y métodos estáticos.
La lógica es la misma: los métodos estáticos y las variables pueden existir y ser llamados o referenciados incluso en ausencia de un objeto.
Pero sin un objeto de la clase externa, no tendremos acceso a la clase interna.
¡Una clara contradicción! Esta es la razón por la cual las variables y los métodos estáticos no están permitidos en las clases internas.
El compilador generará un error si intenta crearlos:
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); } } }
-
Al crear un objeto de una clase interna, su modificador de acceso es importante.
Una clase interna se puede marcar con los modificadores de acceso estándar:
public
,private
,protected
ypackage private
.¿Por qué importa esto?
Esto afecta dónde podemos crear instancias de la clase interna en nuestro programa.
Si nuestra
Seat
clase se declara comopublic
, podemos crearSeat
objetos en cualquier otra clase. El único requisito es que también debe existir un objeto de la clase externa.Por cierto, esto ya lo hicimos aquí:
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(); } }
Obtuvimos acceso fácilmente a la
Handlebar
clase interna desde laMain
clase.Si declaramos la clase interna como
private
, podremos crear objetos solo dentro de la clase externa.Ya no podemos crear un
Seat
objeto "en el exterior":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(); } }
Probablemente ya entiendas la lógica :)
-
Los modificadores de acceso para las clases internas funcionan igual que para las variables ordinarias.
El
protected
modificador proporciona acceso a una variable de instancia en subclases y clases que están en el mismo paquete.protected
también funciona para clases internas. Podemos crearprotected
objetos de la clase interna:- en la clase exterior;
- en sus subclases;
- en clases que están en el mismo paquete.
Si la clase interna no tiene un modificador de acceso (
package private
), se pueden crear objetos de la clase interna:- en la clase exterior;
- en clases que están en el mismo paquete.
Ha estado familiarizado con los modificadores durante mucho tiempo, así que no hay problemas aquí.
GO TO FULL VERSION