Está en la página 1de 5

Clases anidadas

Clases anidadas
• Hay muchas situaciones en las que la existencia de los objetos de una clase
se justifica únicamente por la existencia de objetos de otra clase (eg. los
nodos de una lista enlazada y los iteradores sobre dicha lista).
• En otros casos, es conveniente dotar a algunos objetos de un contexto global
Laboratorio de Programación III común (algo parecido a lo que ocurre con los ámbitos léxicos en lenguajes
como Pascal). Esta situación ocurre, por ejemplo, con los manejadores de
Curso 2001 - 2002 eventos en interfaces gráficas (precisan de un contexto global común a
Profesor: José Luis Sierra través del cuál comunicarse fácilmente).
• En todas estas situaciones es conveniente, además, relajar los criterios de
accesibilidad para estas clases estrechamente relacionadas (algo parecido a
lo que ocurre con las clases amigas en C++).
• Java proporciona un mecanismo para tratar con todas estas situaciones: las
clases anidadas.

Clases anidadas Clases anidadas


• Las clases anidadas pueden declararse en Clase Anidada • La relación entre clases anidadas y clases contenedoras es
cualquier sitio donde pueda realizarse
una declaración (no únicamente dentro de especial.
otras clases). No obstante, normalmente
tienen sentido como miembros de otras – El código de las clases anidadas puede acceder a
clases (clases contenedoras). Clase Anidada Clase Interna cualquier miembro de las clases contenedoras. Dicho
• Las clases anidadas no estáticas se estática acceso debe tener sentido. Así, por ejemplo, las clases
denominan clases internas. Las instancias
de dichas clases únicamente tienen sentido anidadas estáticas únicamente pueden acceder a
en el contexto de instancias (directas o Clase miembros estáticos de la clase contenedora.
indirectas) de las clases contenedoras. Anónima
• Es posible crear instancias de clases – Y lo mismo ocurre con las clases contenedoras respecto
internas de forma anónima. Tales usos a las clases contenidas.
definen clases anónimas.

3 4
Clases anidadas estáticas Clases anidadas estáticas

• Este uso permite concebir las clases contenedoras como módulos que class Aritmetica
class Aritmetica {{
public interface
interface Expresion
Expresion {{
encapsulan las clases internas. public
double evalua();
evalua();
double
• Esta es la única forma de controlar realmente el uso de una clase: si }}
una clase interna estática se declara privada, únicamente podrá ser abstract protected
abstract protected static
static class
protected Expresion
Expresion op1;
class ExpresionBinaria
op1;
ExpresionBinaria implements
implements Expresion{
Expresion{

utilizada en el código de la clase contenedora. Los paquetes ofrecen protected


protected Expresion
Expresion op2;
op2;
protected
reglas de visibilidad más débiles. public ExpresionBinaria(Expresion
public ExpresionBinaria(Expresion op1,op1, Expresion
Expresion op2)
op2) {{
this.op1 == op1;
op1;
• Las clases anidadas estáticas sólo pueden acceder a miembros this.op1
this.op2 == op2;
op2;
estáticos de las clases que las contienen. }}
this.op2

• Las interfaces también pueden aparecer anidadas en clases o en }}


otras interfaces. Tales ocurrencias son siempre estáticas (aunque no abstract protected
protected static
static class
class ExpresionUnaria
ExpresionUnaria implements
implements Expresion{
Expresion{
abstract
se indique static). protected Expresion
protected Expresion op;
op;
public ExpresionUnaria(Expresion
ExpresionUnaria(Expresion op)op) {{
• Las interfaces pueden tener también clases e interfaces como public
this.op == op;
op;
this.op
miembros. Tales miembros han de ser siempre estáticos. }}
}} }}

5 6

Clases anidadas estáticas Clases anidadas estáticas

class Operaciones
class Operaciones extends
extends Aritmetica
Aritmetica {{
public static
public static class
class Suma
Suma extends
extends ExpresionBinaria
ExpresionBinaria {{ public static
static void
void main(String[]
main(String[] args)
args) {{
public
public Suma(Expresion
public Suma(Expresion op1,
op1, Expresion
Expresion op2)
op2) {{ Aritmetica.Expresion ee ==
Aritmetica.Expresion
super(op1,op2);
super(op1,op2); new Operaciones.Suma(
Operaciones.Suma(
new
}} new Operaciones.Neg(new
new Operaciones.Neg(new Operaciones.Num(5)),
Operaciones.Num(5)),
public double
public double evalua()
evalua() {{ new Operaciones.Num(6));
Operaciones.Num(6));
new
return op1.evalua()
return op1.evalua() ++ op2.evalua();
op2.evalua(); System.out.println(e.evalua());
System.out.println(e.evalua());
}} }}
}}
public static
public static class
class Neg
Neg extends
extends ExpresionUnaria
ExpresionUnaria {{
public Neg(Expresion
public Neg(Expresion op)op) {{
super(op);
super(op);
}}
public double
public double evalua()
evalua() {{
return -- op.evalua();
return op.evalua();
}}
}}
...
...
}}

7 8
Clases Internas Clases Internas
interface Enumeration
interface Enumeration {{
• Las instancias de las clases internas (clases anidadas no estáticas) sólo public boolean
public boolean hasMoreElements();
hasMoreElements();
tienen sentido en el contexto de las instancias de las clases contenedoras. public Object
public Object nextElement();
nextElement();
}}
Objeto contenedor public class
class Stack
Stack {{
public
private Vector
private Vector items;
items;
Objeto interno Esta clase permite adaptar
...//código para
...//código para la
la pila
pila ...
... Stack a la interfaz
• Desde las clases internas son visibles todos los miembros de la clase Enumeration. Si Stack
public Enumeration
Enumeration enumerator()
enumerator() {{ implementara directamente
contenedora (independientemente de sus modificadores). public
return new
return new StackEnum();
StackEnum(); Enumeration, no podría
• Una clase interna no puede tener (y no tiene sentido que tenga) miembros }} tenerse simultáneamente
estáticos. private class
private class StackEnum
StackEnum implements
implements Enumeration
Enumeration {{ múltiples iteradores sobre
int currentItem
currentItem == items.size()
items.size() -- 1;
1; las pilas.
• Cuando una clase interna se declara dentro de un método (esto tiene, sobre int
public boolean
boolean hasMoreElements()
hasMoreElements() {{
public
todo, sentido para las clases anónimas), dentro de la clase puede verse return (currentItem
(currentItem >=
>= 0);
0);
return
también variables locales del método, pero éstas tienen que haber sido }}
declaradas finales, y se han debido inicializar antes de su uso en la clase public Object
public Object nextElement()
nextElement() {{
interna. if (!hasMoreElements())
if (!hasMoreElements()) throw
throw new
new NoSuchElementException();
NoSuchElementException();
else return
return items.elementAt(currentItem--);
items.elementAt(currentItem--);
• Las clases internas son especialmente útiles para implementar adaptadores }}}
else
(clases que permiten a otra clase ajustarse a una interfaz). 9 }}} 10

Clases Internas Clases Internas


class MaquinaP {
private Stack pilaEvaluacion; //continuación MáquinaP
public MaquinaP() { public class Cte implements Instruccion {
pilaEvaluacion = new Stack(); private double c;
} public Cte(double c) {this.c = c;}
protected void apila(double v) { public void ejecuta() {apila(c);}
pilaEvaluacion.push(new Double(v)); } Todas las instrucciones
} public class Suma implements Instruccion { se ejecutan en el
protected double desapila() { public void ejecuta() {
contexto común de una
return ((Double)pilaEvaluacion.pop()).doubleValue(); double v2 = desapila();
} double v1 = desapila(); máquina de evaluación
protected double cabeza() { apila(v1+v2); basada en una pila
return ((Double)pilaEvaluacion. }
peek()).doubleValue(); }
} public class Resta implements Instruccion {
protected interface Instruccion { public void ejecuta() {
void ejecuta(); double v2 = desapila();
} double v1 = desapila();
public double ejecuta(Instruccion[] programa) { apila(v1-v2);
for (int i=0; i < programa.length; i++) }
programa[i].ejecuta(); }
return cabeza(); }
} 11 12
// continua
Clases Internas Clases Anónimas

• Muchas veces es necesario utilizar una implementación de una interfaz o una


variante de una clase en un único sitio (eg. manejadores de eventos en frameworks de
construcción de interfaces de usuario, visitantes auxiliares ☺, etc.).
public class
public class PruebaMaquinaP
PruebaMaquinaP {{
public static void main(String[]
main(String[] args)
args) {{ • En estos casos, definir explícitamente la clase puede resultar pesado.
public static void • Para ello Java introduce el concepto de clase anónima: la clase se define cuando se
MaquinaP maquina
MaquinaP maquina == new
new MaquinaP();
MaquinaP();
MaquinaP.Instruccion[] programa
programa == instancia el objeto de dicha clase.
MaquinaP.Instruccion[] • Las clases anónimas es el análogo en Java a las funciones lambda en programación
{maquina.new Cte(5),maquina.new Cte(6),
{maquina.new Cte(5),maquina.new Cte(6),
maquina.new Cte(7),maquina.new
Cte(7),maquina.new Resta(),
Resta(), funcional.
maquina.new • Importante: aunque las clases anónimas pueden ser convenientes en muchos casos,
maquina.new Suma()};
maquina.new Suma()};
System.out.println(maquina.ejecuta(programa)); su uso indiscriminado puede conducir a un código más difícil de leer. Deben usarse
System.out.println(maquina.ejecuta(programa)); de forma razonada, en contextos donde su uso esté bien claro (eg. asociación de un
}} manejador de eventos para un botón).
}} • Las clases anónimas son, en realidad, clases internas (y Java las trata como tal). Las
únicas restricciones son (i) que no es posible pasar argumentos en el constructor
cuando se instancia una clase anónima (siempre se invoca al constructor por defecto)
y (ii) que la clase anónima únicamente hereda (o implementa) una clase (interfaz): la
indicada en la construcción de la instancia.

13 14

Clases Anónimas Clases Anónimas

public class
public class Stack
Stack {{
private Vector
private Vector items;
items; interface Lambda
interface Lambda {{
Object aplica(Object
Object aplica(Object valor);
valor);
...//código para
para la
la pila
pila ...
...
...//código Creación de una }}
public Enumeration
Enumeration enumerator()
enumerator() {{ instancia de una clase
public
return new
new Enumeration()
Enumeration() {{ anónima que
return abstract class
class UtilidadesLista
UtilidadesLista {{
int currentItem
int currentItem == items.size()
items.size() -- 1;
1; implementa la interfaz abstract
public boolean
boolean hasMoreElements()
hasMoreElements() {{ Enumertation abstract protected
abstract protected List
List creaLista();
creaLista();
public
return (currentItem
return (currentItem >=
>= 0);
0); ...
...
}} public List
public List map(List
map(List l,Lambda
l,Lambda f)
f) {{
public Object
public Object nextElement()
nextElement() {{ List res
res == creaLista();
creaLista();
if (!hasMoreElements())
(!hasMoreElements()) List
if for (int
(int i=0;
i=0; ii << l.size();
l.size(); i++)
i++)
throw new
new NoSuchElementException();
NoSuchElementException(); for
throw res.add(i,f.aplica(l.get(i)));
else return
else return items.elementAt(currentItem--);
items.elementAt(currentItem--); res.add(i,f.aplica(l.get(i)));
}}; return res;
return res;
}};
}} }}
}} ...
...
}}
15 16
Clases Anónimas Clases Anónimas

...
Ejemplos de algunos usos comunes de clases anónimas en
...
// numeros
// numeros es
es una
una lista
lista que
que contiene
contiene objetos
objetos dede tipo
tipo Integer.
Integer. interfaces gráficas.
List incrementada
List incrementada ==
...
...
new UtilidadesLista()
new UtilidadesLista() {{
JButton exit
JButton exit == new
new JButton("exit");
JButton("exit");
protected List
protected List creaLista()
creaLista() {{
exit.addActionListener(
exit.addActionListener(
return new
return new ArrayList();
ArrayList();
new ActionListener()
new ActionListener() {{
}} public void
void actionPerformed(ActionEvent
actionPerformed(ActionEvent ev)
ev) {{
}.map(numeros, public
}.map(numeros, System.exit(0);
new Lambda()
Lambda() {{ System.exit(0);
new }});
public Object
Object aplica(Object
aplica(Object v)v) {{ }});
public ...
return new
new Integer(((Integer)
Integer(((Integer) v).intValue()+1);
v).intValue()+1); ...
return JFrame ff == new
new JFrame();
JFrame();
JFrame
}} f.addWindowListener(
}); f.addWindowListener(
}); new WindowAdapter()
WindowAdapter() {{
new
public void
public void windowClosing(WindowEvent
windowClosing(WindowEvent ev)
ev) {{
System.exit(0);
System.exit(0);
}});
}});
...
...
17 18

También podría gustarte