Aprenda Java para Desarrollo en Android: Clases Internas
Spanish (Español) translation by Rodney Martinez (you can also view the original English article)
En este tutorial usted se familiarizará con los conceptos de clases internas en Java —son esas clases cuyos alcances y definición están englobadas dentro de otra clase. Además aprenderá acerca de las clases internas anónimas, que son utilizadas con bastante frecuencia cuando está desarrollando con el Android SDK.
Las aplicaciones de Android están escritas en Java, un lenguaje de programación orientado a objetos. En este tutorial aprenderá sobre las clases internas; cuando y porqué usarlas y cómo funcionan. También aprenderá cómo crear nuevos objetos de forma dinámica usando clases internas anónimas.
Qué necesitará
Técnicamente, no necesita ninguna herramienta para completar este tutorial, sin embargo, ciertamente las necesitará para desarrollar aplicaciones para Android.
Para desarrollar aplicaciones para Android (o para cualquier aplicación en Java, por ende), usted necesita un ambiente de desarrollo para escribir y construir aplicaciones. Eclipse es un ambiente de desarrollo (IDE) muy popular para Java y el preferido IDE para el desarrollo en Android. Es gratuito y está disponible para los sistemas operativos: Windows, Mac y Linux.
Para completar instrucciones sobre cómo instalar Eclipse (incluyendo las versiones que tienen soporte) y el Android SDK, vea el sitio web para el desarrollo en Android.
¿Qué es una Clase Interna?
La mayoría de las clases en Java son clases de alto nivel. Estas clases y los objetos que definen son independientes. Además, usted puede anidar clases para, obviamente, encapsular y definir objetos subordinados que sólo importan en el contexto de las clases externas. Las clases anidadas son llamadas clases internas.
Las clases internas pueden tener todo las características de una clase regular, pero su alcance es limitado. Las clases internas tienen otra ventaja: ellas tienen acceso completo a las clases en las cuales están anidadas —está característica, hace las clases internas perfectas para implementar adaptar la funcionalidad como los iteradores.
Aquí está un ejemplo de la clase de alto nivel con dos clases internas:
1 |
|
2 |
public class User {
|
3 |
|
4 |
// User fields, including variables of type LoginInfo and UserPreferences |
5 |
// Misc user methods |
6 |
|
7 |
class LoginInfo |
8 |
{
|
9 |
// Login info fields |
10 |
// Login/Logout methods |
11 |
// Can access User fields/methods |
12 |
} |
13 |
|
14 |
class Preferences |
15 |
{
|
16 |
// User preference fields |
17 |
// Get/Set preference methods |
18 |
// Reset preferences method |
19 |
// Can access User fields/methods |
20 |
} |
21 |
} |
En este ejemplo, la clase Usuario tiene dos clases internas: LoginInfo y Preferences. Si bien todos los datos y funcionalidad relacionada con los usuarios podría estar definida en la clase Usuario, usando las clases internas para separar la funcionalidad, puede hacer que el código sea más fácil de leer y mantener. Las clases internas LoginInfo y Preferences, también tienen accesos a los campos protected/private y a los métodos disponibles dentro de la clase Usuario, la cual podría no tener debido a la seguridad, si fueran definidas como clases independientes.
Aunque, es importante recordar que las clases internas, en realidad, solamente existen para ayudar a los desarrolladores a organizar el código, compilar los hilos de las clases internas como con cualquier otra clase, excepto que las clases internas tiene un alcance limitado y están, por lo tanto, amarradas a clase en la que están definidas. Dicho de otra manera, usted no sería capaz de usar o instanciar las clases LoginInfo o Preferences excepto con una instancia de la clase Usuario, pero las clases internas podrían acceder a cualquier campo o método disponible en las clase externa Usuario, según lo necesite.
Usando clases static anidadas
Un uso particular para las clases anidadas es las clases static anidadas. Una clase static anidada define el funcionamiento que no está unido a una instancia de un objeto específico, pero aplica en todas las instancias. Por ejemplo, podríamos añadir una tercera clase añadida, esta vez static, para la clase Usuario para controlar la funcionalidad relacionada al servidor.
1 |
|
2 |
public class User {
|
3 |
|
4 |
// User fields, including variables of type LoginInfo and UserPreferences |
5 |
// Misc user methods |
6 |
|
7 |
class LoginInfo {}
|
8 |
|
9 |
public static class ServerInfo {}
|
10 |
{
|
11 |
// Server info applies to all instances of User |
12 |
} |
13 |
} |
Debido a que es public, esta clase static anidada puede ser instanciada usando la siguiente declaración nueva:
1 |
|
2 |
User.ServerInfo sInfo = new User.ServerInfo(); |
La clase externa no tiene que ser instanciada para realizar esta instanciación, por tanto el uso del nombre de la clase. En cuanto a la clase static anidad, a diferencia de la clase static no anidada (acá la clase interna) no tiene acceso a miembros de la clase externa -ni siquiera pueden instanciarla.
El Poder de las Clases Anónimas Internas
Adroid, usa las clases anónimas internas para magníficos efectos. Las clases anónimas internas son básicamente abreviaturas de los desarrolladores, que le permiten al desarrollador; crear, definir y usar un objeto personalizado todo en una "línea". Usted puede haber visto ejemplos del uso de las clases anónimas internas en muestras de códigos y, tal vez, ni se ha dado cuenta.
Para crear una clase anónima interna, solamente proporcione el lado derecho de la definición. Comience con la palabra clave new, seguido por la clase o interface que desea ampliar o implementar, seguido por la definición de clase. Esto creará la clase y lo devolverá como un valor que puede usar luego para llamar un método.
Cuando usamos una clase anónima interna, el objeto creado no tiene asignado un nombre (de ahí el término anónimo). El lado del efecto, por supuesto, es que el objeto es utilizado solamente una vez. Por ejemplo, es común usar una clase anónima interna para construir una versión personalizada de un objeto como un valor de retorno. Por ejemplo, aquí ampliamos la clase Truck (suponiendo fue definida en otro lugar y que tiene una campo llamado mpg y dos métodos; start() y stop():
1 |
|
2 |
Truck getTruck() |
3 |
{
|
4 |
return new Truck() |
5 |
{
|
6 |
int mpg = 3; |
7 |
void start() { /* start implementation */ }
|
8 |
void stop() { /* stop implementation */ }
|
9 |
}; |
10 |
} |
Ahora veamos unos ejemplos prácticos de clases anónimas internas usadas en Android.
Usando una Clase Anónima Interna para definir un Listener
Los desarrolladores de Android, a menudo usan clases anónimas internas para definir listeners especializados, que registran devolución de llamadas para funcionamientos específicos cuando un evento ocurre. Por ejemplo, para registrar los clicks en un control View, el desarrollador debe llamar al método setOnClickListener(), que toma un solo parámetro: un objeto View.OnClickListener.
Los desarrolladores, habitualmente usan la técnica de la clase anónima interna para crear, definir y usar sus objetos personalizados View.OnClickListener, así:
1 |
|
2 |
Button aButton = (Button) findViewById(R.id.MyButton); |
3 |
aButton.setOnClickListener(new View.OnClickListener() {
|
4 |
public void onClick(View v) {
|
5 |
// User clicked my button, do something here! |
6 |
} |
7 |
}); |
Usando una Clase Anónima Interna para Iniciar un Hilo
Vamos a ver otro ejemplo. Es bastante común definir nuevas clases Hilos [Thread] proporcionan la implementación de sus métodos run() e inicia ese hilo de un solo:
1 |
|
2 |
new Thread() {
|
3 |
public void run() |
4 |
{
|
5 |
doWorkHere(); |
6 |
} |
7 |
}.start(); |
Usando Clases Internas Designadas
Al usar las clases anónimas internas para listeners en Android, es tan común que es prácticamente su segunda naturaleza hacer eso: ¿Por qué, entonces, no querías usarlos? Vamos a responder a esta pregunta a través de ejemplos hipotéticos.
Digamos que tiene una pantalla que tiene 100 botones en ella (dijimos hipotéticamente, ¿de acuerdo?). Ahora, digamos que cada botón, cuando es presionado hace, exactamente, lo mismo. En este caso, solamente escucharemos los clicks y Toast el texto desde el objeto View aprobado (el texto muestra en el Botón que fue cliqueado).
Aquí está un pseudo código para hacer eso:
1 |
|
2 |
Button[] buttons = getAllOneHundredButtonsAsArray(); |
3 |
for (Button button : buttons) {
|
4 |
button.setOnClickListener(new View.OnClickListener() {
|
5 |
public void onClick(View v) {
|
6 |
showToast(v.getText()); |
7 |
} |
8 |
}); |
9 |
} |
Corto y elegante, ¿qué tiene de malo? En cada iteración, un objeto nuevo OnClickListener es instanciado. Ya que cada uno es exactamente el mismo, no hay buenas razones para crear 100 de ellos. En vez de eso, usted puede crear un sola clase interna, nombrada; intanciarla una vez, luego pasar eso al método setOnClickListener(). Por ejemplo:
1 |
|
2 |
class MyActivity extends Activity {
|
3 |
|
4 |
public void myMethod() {
|
5 |
MyClickHandler handler = new MyClickHandler(); |
6 |
Button[] buttons = getAllOneHundredButtonsAsArray(); |
7 |
for (Button button : buttons) {
|
8 |
button.setOnClickListener(handler); |
9 |
} |
10 |
} |
11 |
|
12 |
class MyClickHandler implements View.OnClickListener {
|
13 |
public void onClick(View v) {
|
14 |
showToast(((Button) v).getText()); |
15 |
} |
16 |
} |
17 |
} |
Si usted prefiere anonimato, todavía puede asignar un clase anónima interna a una variable y usar al igual que:
1 |
|
2 |
class MyActivity extends Activity {
|
3 |
|
4 |
public void myMethod() {
|
5 |
View.OnClickListener handler = new View.OnClickListener() {
|
6 |
public void onClick(View v) {
|
7 |
showToast(((Button) v).getText()); |
8 |
} |
9 |
}; |
10 |
|
11 |
Button[] buttons = getAllOneHundredButtonsAsArray(); |
12 |
for (Button button : buttons) {
|
13 |
button.setOnClickListener(handler); |
14 |
} |
15 |
} |
16 |
} |
El método depende de usted, pero tenga en mente la memoria potencial y las cuestiones de desempeño que instanciará un montón de objetos.
Una nota de matices
Este tutorial fue diseñado para se una guía de introducción para las clases internas en Java. Hay estilos de consideraciones y matices cuando usamos las clases internas en diferentes formas creativas. Incluso más allá de eso, usted puede explorar más para aprender más sobre los efectos internos y las diferencias marginales de desempeño que pueden aparecer cuando usted usa clases anidadas en diferentes maneras. Todo esto, sin embargo, está más allá del alcance de este tutorial.
Una nota rápida sobre Terminología
Aunque hemos tratado de ser consistentes con la terminología sobre anidamiento y las clases internas, la terminología no es consistente siempre, por lo menos, desde varios recursos en línea y fuera de línea. La siguiente son una lista de los términos de la documentación actual de Sun/Oracle, que es tan buena como cualquiera que ha sido autorizada en Java:
- Clase Anidada: una clase definida dentro de otra clase.
- Clase Static Anidada: una clase static definida dentro de otra clase.
- Clase Interna: una clase no static anidada definida dentro de otra clase.
- Clase Interna Local: una clase definida dentro de un método.
- Clase Interna Anónima: una clase sin nombrar definida dentro de un método.
¿Confundido? Puede usar los métodos llamados java.lang.Class y isLocalClass() y la clase isAnonymous() en clases instanciadas para determinar un par de estas propiedades. Una entrada de blog de Oracle intento aclarar la situación un poco, pero también, con un diagrama de Venn.
En conclusión
El lenguaje de programación Java tiene soporte para las clases anidadas, lo cual le permite a los desarrolladores gran flexibilidad en la definición de objetos. Las clases internas pueden ser usadas para organizar la funcionalidad de las clases o para definir funcionamientos especializados que, de otro modo, requeriría el desarrollo para clases expuestas a datos y funcionalidades que, en verdad, no serían expuestas. Las clases internas static pueden ser utilizadas para definir campos y la funcionalidad que aplica a todas las instancias de una clase. Finalmente, las clases internas anónimas proporcionan una abreviatura útil para los desarrolladores que quieren crear, definir y usar un objeto personalizado de una sola vez.



