Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Java 8

Java 8 para el desarrollo en Android: Métodos estáticos y predeterminados

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Java 8 for Android Development.
Java 8 for Android: Cleaner Code With Lambda Expressions
Java 8 for Android Development: Stream API and Date & Time Libraries

Spanish (Español) translation by Ana Paulina Figueroa Vazquez (you can also view the original English article)

Java 8 fue un gran paso adelante para ese lenguaje de programación y ahora, con el lanzamiento de Android Studio 3.0, los desarrolladores de Android finalmente tienen acceso al soporte integrado para algunas de las características más importantes de Java 8.

En esta serie de tres partes exploraremos las características de Java 8 que puedes comenzar a usar en tus proyectos de Android hoy mismo. En Código más limpio con expresiones Lambda, configuramos nuestro desarrollo para usar el soporte de Java 8 proporcionado por la cadena de herramientas predeterminada de Android antes de analizar las expresiones lambda a profundidad.

En esta publicación analizaremos dos formas diferentes en las que puedes declarar métodos no abstractos en tus interfaces (algo que no era posible en versiones anteriores de Java). También responderemos a la pregunta: ahora que las interfaces pueden implementar métodos, ¿cuál es exactamente la diferencia entre clases abstractas e interfaces?.

También cubriremos una función de Java 8 que te brinda la libertad de usar la misma anotación tantas veces como quieras en la misma ubicación y, al mismo tiempo, seguir siendo compatible con las versiones anteriores de Android.

Pero primero echemos un vistazo a una característica de Java 8 que está diseñada para ser usada en combinación con las expresiones lambda que vimos en la publicación anterior.

Escribe expresiones lambda más limpias con referencias a métodos

En la última publicación viste cómo puedes usar expresiones lambda para eliminar una gran cantidad de código repetitivo de tus aplicaciones Android. Sin embargo, cuando una expresión lambda simplemente está invocando a un único método que ya tiene un nombre, puedes cortar incluso más código de tu proyecto usando una referencia a un método.

Por ejemplo, esta expresión lambda solamente está redireccionando trabajo a un método handleViewClick existente:

En este escenario podemos hacer referencia a este método por su nombre, usando el operador de referencia a métodos ::. Puedes crear este tipo de referencia a métodos usando el siguiente formato:

En nuestro ejemplo Floating Action Button (Botón de acción flotante), podemos usar una referencia a un método como cuerpo de nuestra expresión lambda:

Nota que el método al que se está haciendo referencia debe recibir los mismos parámetros que recibe la interface — en este caso se trata de View.

Puedes usar el operador de referencia a métodos (::) para hacer referencia a cualquiera de los siguientes:

Un método estático

Si tienes una expresión lambda que invoca a un método estático:

Entonces puede convertirla a una referencia a un método:

Por ejemplo, si tuvieras un método estático PrintMessage en una clase MyClass, entonces tu referencia a método se vería más o menos así:

Un método instancia de un objeto específico

Este es un método instancia de un objeto que se conoce con anticipación, lo que te permite reemplazar esto:

Con esto:

De esa forma, si tuvieras la siguiente expresión lambda:

Entonces introducir una referencia a un método daría como resultado lo siguiente:

Un método instancia de un objeto arbitrario de un tipo en particular

Este es un método instancia de un objeto arbitrario que será proporcionado más adelante, y escrito en el siguiente formato:

Referencias a un constructor

Las referencias a un constructor son similares a las referencias a métodos, excepto por el uso de la palabra clave new para invocar al constructor. Por ejemplo, Button::new es una referencia a un constructor de la clase Button, aunque el constructor exacto que está siendo invocado depende del contexto.

A través del uso de referencias a constructores, puedes convertir esto:

En esto:

Por ejemplo, si tuvieras la siguiente interfaz MyInterface:

Entonces podrías usar referencias a constructores para crear nuevas instancias de Student:

También es posible crear referencias a constructores para tipos arreglo. Por ejemplo, una referencia a constructor para un arreglo de tipo int es int[]::new.

Agrega métodos predeterminados a tus interfaces

Antes de Java 8 solamente podías incluir métodos abstractos en tus interfaces (es decir, métodos sin cuerpo), lo que dificultaba la modificación de interfaces después de la publicación.

Cada vez que agregabas un método a la definición de una interfaz, todas las clases que implementaban a esa interfaz de repente sufrían de una implementación faltante. Por ejemplo, si tuvieras una interfaz (MyInterface) usada por MyClass, entonces añadir un método a MyInterface hubiera roto la compatibilidad con MyClass.

En el mejor de los casos, si fueras responsable del pequeño número de clases que usaron a MyInterface, este comportamiento sería molesto pero manejable; simplemente tendrías que reservar un poco de tiempo para actualizar tus clases con la nueva implementación. Sin embargo, las cosas podrían volverse mucho más complicadas si un gran número de clases hubiera implementado MyInterface, o si la interfaz hubiera sido usada en clases de las que no fueras responsable.

Si bien habían varias soluciones para este problema, ninguna de ellas era ideal. Por ejemplo, podías incluir nuevos métodos en una clase abstracta, pero esto aún requería que todos actualizaran sus códigos para extender esta clase abstracta; y, aunque podías extender la interfaz original con una nueva interfaz, cualquier persona que hubiera querido usar esos nuevos métodos hubiera necesitado volver a escribir todas sus referencias a interfaces existentes.

Con la introducción de métodos predeterminados en Java 8, ahora es posible declarar métodos no abstractos (es decir, métodos con un cuerpo) dentro de tus interfaces, de manera que finalmente puedes crear implementaciones predeterminadas para tus métodos.

Cuando agregas un método a tu interfaz como un método predeterminado, no es necesario que todas las clases que implementen a esta interfaz proporcionen sus propias implementaciones, lo que te da una manera de actualizar tus interfaces sin perder la compatibilidad. Si agregas un nuevo método a una interfaz como un método predeterminado, entonces todas las clases que usen esta interfaz, pero que no proporcionen su propia implementación, simplemente heredarán la implementación predeterminada del método. Ya que a la clase no le falta ninguna implementación, esta continuará funcionando de forma normal.

De hecho, la introducción de métodos predeterminados fue la razón por la que Oracle pudo realizar tantas adiciones a la API Collections en Java 8.

Collection es una interfaz genérica que es usada en muchas clases diferentes, así que la adición de nuevos métodos a esta interfaz tenía el potencial de destruir innumerables líneas de código. En vez de añadir nuevos métodos a la interfaz Collection y descomponer todas las clases derivadas de esta interfaz, Oracle creó la característica de métodos predeterminados y luego agregó estos nuevos métodos usando dicha característica. Si echas un vistazo al nuevo método Collection.Stream() (que exploraremos a detalle en la tercera parte), verás que fue añadido como un método predeterminado:

La creación de un método predeterminado es simple — solamente agrega el modificador default a la firma de tu método:

Ahora, si MyClass usa MyInterface pero no proporciona su propia implementación de defaultMethod, solamente heredará la implementación predeterminada proporcionada por MyInterface. Por ejemplo, la siguiente clase aún compilará:

Una clase que implementa puede sobrescribir la implementación predeterminada que proporciona la interfaz, así que las clases aún tienen el control total de sus implementaciones.

Si bien los métodos predeterminados son una adición bienvenida por los diseñadores de APIs, ocasionalmente estos métodos pueden causar un problema para los desarrolladores que intentan usar múltiples interfaces en la misma clase.

Imagina que, además de MyInterface, tienes lo siguiente:

Tanto MyInterface como SecondInterface contienen un método predeterminado con exactamente la misma firma (defaultMethod). Ahora imagina que intentas usar ambas interfaces en la misma clase:

En este punto tienes dos implementaciones en conflicto de defaultMethod, y el compilador no tiene idea de qué método usar, así que te vas a encontrar con un error de compilación.

Una manera de resolver este problema es sobrescribir el método en conflicto con tu propia implementación:

La otra solución es especificar qué versión de defaultMethod quieres implementar, usando el siguiente formato:

Así que si quisieras invocar a la implementación MyInterface#defaultMethod(), entonces usarías lo siguiente:

Usando métodos estáticos en tus interfaces de Java 8

De manera similar a los métodos predeterminados, los métodos estáticos de interfaz te proporcionan una forma de definir métodos dentro de una interfaz. Sin embargo, a diferencia de los métodos predeterminados, una clase que esté implementando no puede sobrescribir los métodos estáticos de una interfaz.

Si tienes métodos estáticos específicos para una interfaz, entonces los métodos estáticos de interfaz de Java 8 te proporcionan una forma de colocar estos métodos dentro de la interfaz correspondiente, en vez de tener que almacenarlos en una clase separada.

Puedes crear un método estático colocando la palabra clave static al principio de la firma del método, por ejemplo:

Cuando implementas una interfaz que contiene un método estático de interfaz, ese método estático aún es parte de la interfaz y no es heredado por la clase que implementa, por lo que necesitarás colocar un prefijo en el método con el nombre de la interfaz, por ejemplo:

Esto también significa que una clase y una interfaz pueden tener un método estático con la misma firma. Por ejemplo, el uso de MyClass.staticMethod y MyInterface.staticMethod en la misma clase no causará un error en tiempo de compilación.

Así que, esencialmente ¿las interfaces son simplemente clases abstractas?

La adición de métodos estáticos de interfaz y métodos predeterminados ha llevado a algunos desarrolladores a preguntarse si las interfaces de Java se están volviendo más como las clases abstractas. Sin embargo, incluso con la adición de métodos estáticos de interfaz y predeterminados, aún existen algunas diferencias notables entre las interfaces y las clases abstractas:

  • Las clases abstractas pueden tener variables finales, no finales, estáticas y no estáticas, mientras que las interfaces solamente pueden tener variables estáticas y finales.
  • Las clases abstractas te permiten declarar campos que no son estáticos ni finales, mientras que los campos de una interfaz son inherentemente estáticos y finales.
  • En las interfaces, todos los métodos que declares o definas como predeterminados son inherentemente públicos, mientras que en las clases abstractas puedes definir métodos concretos públicos, protegidos y privados.
  • Las clases abstractas son clases, y por lo tanto puede tener un estado; las interfaces no pueden tener un estado asociado a ellas.
  • Puedes definir constructores dentro de una clase abstracta, algo que no es posible dentro de las interfaces de Java.
  • Java solamente te permite extender una clase (sin importar si es abstracta), pero eres libre de implementar tantas interfaces como necesites. Esto significa que las interfaces generalmente tienen la ventaja cuando necesitas herencia múltiple, ¡aunque debes tener cuidado con el mortal problema del diamante!.

Aplica la misma anotación tantas veces como lo desees

Tradicionalmente, una de las limitaciones de las anotaciones de Java ha sido que no puedes aplicar la misma anotación más de una vez en la misma ubicación. Intenta usar la misma anotación varias veces y te encontrarás con un error en tiempo de compilación.

Sin embargo, con la introducción de las anotaciones repetidas de Java 8, ahora tienes la libertad de usar la misma anotación tantas veces como quieras en la misma ubicación.

Para garantizar que tu código siga siendo compatible con las versiones anteriores de Java, necesitarás almacenar tus anotaciones repetidas en una anotación contenedora.

Puedes indicarle al compilador que genere este contenedor llevando a cabo los siguientes pasos:

  • Marca la anotación en cuestión con la meta-anotación @Repeatable (una anotación que se usa para anotar una anotación). Por ejemplo, si quisieras hacer que la anotación @ToDo fuera repetible, usarías: @Repeatable(ToDos.class). El valor en paréntesis es el tipo de anotación contenedora que el compilador generará eventualmente.
  • Declara el tipo de la anotación contenedora. Esta debe tener un atributo que es un arreglo del tipo de la anotación repetida, por ejemplo:

Si intentas aplicar la misma anotación múltiples veces sin declarar primero que es repetible, esto ocasionará un error en tiempo de compilación. Sin embargo, una vez que hayas especificado que ésta es una anotación repetible, puedes usarla múltiples veces en cualquier lugar en donde usarías una anotación estándar.

Conclusión

En esta segunda parte de nuestra serie sobre Java 8, vimos cómo puedes eliminar aún más código repetitivo de tus proyectos de Android combinando expresiones lambda con referencias a métodos, además de ver cómo mejorar tus interfaces con métodos estáticos y predeterminados.

En la tercera y última entrega, veremos una nueva API de Java 8 que te permite procesar enormes cantidades de datos de una manera más eficiente y declarativa, sin tener que preocuparte por la concurrencia y la gestión de hilos. También probaremos algunas de las diferentes herramientas que hemos discutido a lo largo de esta serie, explorando el rol que juegan las interfaces funcionales en las expresiones lambda, los métodos estáticos de interfaz, los métodos predeterminados y más.

Y finalmente, a pesar de que todavía estamos esperando que la nueva API de fecha y hora de Java 8 llegue oficialmente a Android, te enseñaré cómo puedes comenzar a usar esta nueva API en tus proyectos de Android hoy mismo, con la ayuda de algunas bibliotecas de terceros.

¡Mientras tanto consulta algunas de nuestras otras publicaciones sobre Java y el desarrollo de aplicaciones en Android!.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.