Skip to main content

Cotas en Java

⏱ DedicaciĂłn recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


r8vnhill/

Al igual que en Kotlin, en Java también podemos restringir los tipos genéricos usando cotas superiores, permitiendo que un genérico solo acepte tipos que sean subtipos de una clase o que implementen una interfaz específica. Sin embargo, la forma en que esto se declara y se utiliza es ligeramente diferente.

Ejemplo de Cotas Superiores en Java​

Supongamos que estamos desarrollando una biblioteca de persistencia y queremos crear una clase Repository que solo acepte tipos que implementen la interfaz Entity. En Java, lo harĂ­amos de la siguiente manera:

interface Entity {
int getId();
}

public class Repository<T extends Entity> {
private List<T> entities = new ArrayList<>();

public void save(T entity) {
entities.add(entity);
}

public List<T> getEntities() {
return entities;
}
}
¿Qué acabamos de hacer?

En Java, la sintaxis para declarar una cota superior es T extends Entity, lo que significa que T debe ser un subtipo de Entity o implementar esta interfaz. Esto restringe el tipo genérico T a aquellos que cumplen con la interfaz Entity.

MĂșltiples Restricciones en Java​

En Java, al igual que en Kotlin, podemos aplicar mĂșltiples restricciones en los parĂĄmetros genĂ©ricos. Sin embargo, Java tiene una forma mĂĄs limitada de hacerlo: solo se permite una clase como cota superior, y el resto de restricciones deben ser interfaces.

Ejemplo de MĂșltiples Restricciones en Java​

Si queremos aplicar mĂșltiples restricciones en Java, la sintaxis es la siguiente:

interface Entity {
int getId();
}

interface Serializable {
String serialize();
}

public class Repository<T extends Entity & Serializable> {
private List<T> entities = new ArrayList<>();

public void save(T entity) {
entities.add(entity);
}

public String serializeAll() {
return entities.stream()
.map(T::serialize)
.collect(Collectors.joining(", "));
}
}
¿Qué acabamos de hacer?

En este caso, T extends Entity & Serializable significa que T debe ser un subtipo de Entity y también implementar la interfaz Serializable. De esta manera, cualquier tipo que se utilice con esta clase cumplirå ambas restricciones.

Cotas Inferiores en Java​

Java, a diferencia de Kotlin, sí soporta cotas inferiores de manera nativa utilizando la palabra clave super. Esto permite declarar que un tipo genérico debe ser un supertipo de otro, proporcionando una mayor flexibilidad cuando se trabaja con herencia.

Ejemplo de Cotas Inferiores en Java​

En Java, podemos utilizar cotas inferiores en los métodos de esta manera:

public class NotificationHandler<N extends Notification> {
private N notification;

public void setNotification(N notification) {
this.notification = notification;
}
}

public class EmailNotification extends Notification {}

public class NotificationSystem {

public void registerHandler(NotificationHandler<? super EmailNotification> handler) {
// handler puede ser un NotificationHandler de cualquier supertipo de EmailNotification
}
}
¿Qué acabamos de hacer?

En este ejemplo, NotificationHandler<? super EmailNotification> significa que handler puede ser un NotificationHandler de cualquier supertipo de EmailNotification. Esto permite una mayor flexibilidad al trabajar con tipos genéricos en Java.

Resumen comparativo​

CaracterĂ­sticaJavaKotlin
Cotas superioresUsa extends para definir una cota superior en un genĂ©rico. Solo acepta subtipos de una clase o interfaz especĂ­fica.Similar, pero con la sintaxis T : UpperBound. Ofrece mayor flexibilidad con la clĂĄusula where para mĂșltiples cotas.
MĂșltiples restriccionesPermite mĂșltiples restricciones con una clase y varias interfaces (T extends Class & Interface).Usa la clĂĄusula where para mĂșltiples restricciones, ofreciendo una forma mĂĄs explĂ­cita y legible.
Cotas inferioresSoporte nativo para cotas inferiores (super), lo que permite definir supertypos en genéricos.No tiene soporte nativo para cotas inferiores, pero puede emularse con contravarianza (in).
VarianzaUsa comodines (? extends, ? super) para manejar varianza en genéricos. Menos flexible y seguro que Kotlin.Ofrece varianza declarativa con in y out, lo que permite un manejo mås robusto y seguro de subtipos y supertipos.
Flexibilidad de APIsLas cotas inferiores nativas permiten mayor flexibilidad en ciertas APIs, especialmente cuando se trabaja con jerarquĂ­as de tipos.Aunque no tiene cotas inferiores nativas, la varianza en sitio de uso y la clĂĄusula where brindan flexibilidad comparable.

Beneficios y limitaciones​

Beneficios

  • Soporte nativo para cotas inferiores: Java permite definir cotas inferiores usando super, lo que facilita la creaciĂłn de APIs que acepten supertipos, brindando mayor flexibilidad en la manipulaciĂłn de jerarquĂ­as de tipos.
  • Manejo robusto de herencia: Las cotas superiores (extends) y cotas inferiores (super) permiten controlar de manera precisa los tipos en una jerarquĂ­a, asegurando que las operaciones solo se realicen sobre tipos compatibles.
  • MĂșltiples restricciones en genĂ©ricos: Java permite imponer mĂșltiples restricciones en genĂ©ricos usando la combinaciĂłn de una clase y varias interfaces (T extends Class & Interface), lo que ayuda a estructurar mejor las clases genĂ©ricas.
  • Compatibilidad y madurez: El sistema de generics de Java ha sido ampliamente utilizado y optimizado a lo largo del tiempo, por lo que cuenta con un soporte sĂłlido y una extensa documentaciĂłn en la comunidad.

Limitaciones

  • Uso de comodines menos seguro: Los comodines (? extends, ? super) en Java pueden ser menos seguros y mĂĄs confusos que la varianza declarativa que ofrecen otros lenguajes como Kotlin, lo que puede llevar a errores o limitaciones en ciertas aplicaciones.
  • Complejidad en la comprensiĂłn: Aunque el soporte para cotas inferiores y superiores es Ăștil, las declaraciones de genĂ©ricos con mĂșltiples restricciones o comodines pueden volverse difĂ­ciles de leer y entender, lo que complica el mantenimiento del cĂłdigo.
  • Mensajes de error menos claros: Los mensajes de error relacionados con las restricciones de tipos en genĂ©ricos pueden ser difĂ­ciles de interpretar en Java, lo que puede complicar la depuraciĂłn y hacer que el proceso de desarrollo sea mĂĄs lento.

ÂżQuĂ© aprendimos?​

En esta lecciĂłn, exploramos las cotas superiores y cotas inferiores en Java, comparĂĄndolas con Kotlin.

Puntos clave​

  1. Cotas superiores: Java usa extends para restringir genéricos a subtipos, mientras que Kotlin ofrece mayor flexibilidad con la clåusula where.
  2. Cotas inferiores: Java soporta nativamente super, lo que facilita el manejo de supertipos en genéricos. Kotlin no tiene soporte directo, pero se puede emular usando varianza con in.
  3. Varianza: Java utiliza comodines (? extends, ? super), menos seguros que la varianza declarativa (in, out) de Kotlin.

Java ofrece cotas inferiores nativas, pero Kotlin proporciona un sistema mås seguro y flexible para manejar genéricos.

BibliografĂ­as Recomendadas

  • 📚 "Generics<T>". (2022). C. Ullenboom, en Java: The comprehensive guide, (pp. 619–660.) Rheinwerk Publishing/SAP Press.