Estoy tratando de hacer una función que los procesos ordenados listas de elementos (comparables). Por lo tanto, estoy usando el genérico <T extends List<? extends Comparable>>
, que obras, siempre y cuando no se necesita ninguna operación de lista específicos que requieren <? extends Comparable>
como entrada. Pero en el siguiente fragmento de código (ejemplo más sencillo: el cálculo de la intersección de dos listas ordenadas) de la línea C.add((Comparable)(A.get(posA)));
es rechazado por el compilador, alegando que add
el argumento necesidades ? extends Comparable
, que Comparable
aparentemente no lo es.
public static <T extends List<? extends Comparable>> T intersect (T A, T B) {
T C = (T) A.getClass().newInstance();
int posA = 0;
int posB = 0;
while(posA<A.size()&&posB<B.size()) {
if (A.get(posA).compareTo(B.get(posB))>0) posB++;
else if (A.get(posA).compareTo(B.get(posB))<0) posA++;
else if (A.get(posA).equals(B.get(posB))) {
C.add((Comparable)(A.get(posA)));
posA++; posB++;
}
}
return C;
}
¿Cómo debo decirle al compilador que A.get(posA)
es de tipo válida ? extends Comparable
? Al parecer, la fundición no funciona, y me gustaría que la rutina para aceptar y listas de elementos de comparación arbitrarias (entero, cadena, objetos personalizados, etc.) volver
¿No te das cuenta todas las declaraciones de tipo inseguras en su código, que varios elencos inseguras?
Usted tiene realmente muchas de ellas. A menudo significa que el enfoque general no es la correcta.
De hecho las cosas no son tan complicadas si usted aprende cómo funcionan los genéricos en Java.
Eso podría ayudarle a:
- https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html
- https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html
Estas son las principales cosas que usted debe considerar de acuerdo a su código real: 1) No usar el tipo cruda como List<? extends Comparable>>
. Comparable
es una clase genérica.
2) No se puede agregar cualquier cosa menos null
en una lista declarada List<? extends Foo>
, que utiliza un comodín acotada superior. El último permite hacer la covariante de lista: aceptar Foo y cualquier subclase pero con la limitación anterior. Por lo que no quiere usar eso.
3) Se puede crear una instancia de un genérico ArrayList
sin la necesidad de declarar el tipo de método genérico T
para el ArrayList
. Utilizando T
para el Comparable
tipo va a hacer cosas realmente simple.
4) ¿Quieres evitar la reflexión tanto como sea posible.
Siguiendo estas ideas se podría escribir un código que podría parecerse a:
public static <T extends Comparable<T>> List<T> intersect (List<T> A, List<T> B) {
List<T> list = new ArrayList<>();
int posA = 0;
int posB = 0;
while(posA<A.size()&&posB<B.size()) {
if (A.get(posA).compareTo(B.get(posB))>0) posB++;
else if (A.get(posA).compareTo(B.get(posB))<0) posA++;
else if (A.get(posA).equals(B.get(posB))) {
list.add(A.get(posA));
posA++; posB++;
}
}
return list;
}
Esa fue mi enfoque original, pero el problema aquí es que no todos los que la intersección de dos listas no ArrayList será un ArrayList aquí.
El tipo de la lista no se conoce en tiempo de compilación si se declara List
para el parámetro. Por lo que inevitablemente terminar con los moldes inseguras.
Por ejemplo :
@SuppressWarnings("unchecked")
public static <T extends Comparable<T>, L extends List<T>> L intersect(L A, L B) {
if (A.getClass() != B.getClass()) {
throw new IllegalArgumentException("not same type between ...");
}
List<T> list = A.getClass()
.newInstance(); // uncheck
int posA = 0;
int posB = 0;
while (posA < A.size() && posB < B.size()) {
if (A.get(posA)
.compareTo(B.get(posB)) > 0)
posB++;
else if (A.get(posA)
.compareTo(B.get(posB)) < 0)
posA++;
else if (A.get(posA)
.equals(B.get(posB))) {
list.add(A.get(posA));
posA++;
posB++;
}
}
return (L) list; // uncheck
}