Estoy luchando para que una implementación de interfaz se herede en otra clase:
Tengo una clase genérica que requiere que se implemente compareTo. La clase se llama GenericList y se implementa como:
public class GenericList<T extends Comparable<T>> { private T[] items; private int count; public GenericList() { items = (T[]) new Comparable[10]; count = 0; } }Tengo una clase de Usuario que implementa compareTo:
public class User implements Comparable<User> { private String email; public User(String email) { this.email = email; } @Override public int compareTo(User o) { return (email.compareTo(o.email)); } }No tengo ningún problema en crear una lista genérica de usuarios:
var users = new GenericList<User>();Si creo una clase que amplía Usuario y trato de crear una lista genérica de ese tipo, aparece un error. He creado una clase llamada Instructor:
public class Instructor extends User{ public Instructor(String email){ super(email); } }Si creo una lista genérica usando esa clase:
var instructors = new GenericList<Instructor>();me sale un error:
Type parameter 'com.martin.Instructor' is not within its bound; should implement 'java.lang.Comparable<com.martin.Instructor>'¿No debería usar el método heredado compareTo?
He intentado muchas formas diferentes, pero no puedo hacer que GenericList use una clase heredada como esta.
Gracias
public class GenericList<T extends Comparable<T>>
Cuando declara GenericList<Instructor> , la declaración anterior reemplaza T con Instructor . Entonces ahora dice que Instructor debe extender (o implementar, realmente) Comparable<Instructor> . El problema es que Instructor extiende User que implementa Comparable<User> pero no implementa Comparable<Instructor> .
Entonces, el problema es mucho antes de intentar encontrar el método heredado compareTo() . Una forma de corregir el error inmediato del compilador es cambiar la declaración de GenericList :
public class GenericList<T extends Comparable<? super T>> Esto utiliza una captura de tipo en la interfaz Comparable .
Ahora, una advertencia justa, verifiqué que este cambio se compilará aquí , pero de lo contrario no lo probé porque su pregunta no proporciona ningún uso de GenericList una vez que lo crea.
La lógica general parece ser correcta: Usuario y Comperable son los supertipos de la clase Instructor, es decir, la instancia de Instructor puede representarlos a ambos. Pero aquí hay una trampa:
var ints1 = (Comparable<Instructor>) new Instructor("name"); // this line will not compline var ints2 = (Comparable<User>) new Instructor("name"); // no errors hereLa primera línea causará un error de compilación porque no hay una implementación de Comparable<Instructor> en la cadena de herencia de la clase Instructor, pero hay una implementación de Comparable<User> que es la clase User en sí. Y, por lo tanto, la firma del método compareTo() que hereda la clase Instructor será compareTo(User user).
Un recordatorio rápido: los genéricos son invariantes, es decir, a una variable de tipo List<Object> podemos asignar solo List<Object>, no List<Person> o List<Cat>. Del mismo modo, los tipos Comparable<User> y Comparable<Instructor> no son comparables entre sí, independientemente del hecho de que User sea un supertipo de Instructor. Eso es lo que se llama invariancia y el objetivo principal es hacer cumplir la seguridad de tipos.
Como conciencia de esto, no es posible forzar el objeto Instructor al tipo Comparable<Instructor> .
Un posible remedio es implementar Comparable en la clase Instructor, será útil solo si Instructor define algunas propiedades propias que establecen la unicidad de sus instancias, o cambia la declaración de tipo genérico como ya se muestra aquí