tipos - salir de un metodo void java
La anulación de un método con un tipo de retorno genérico falla después de agregar un parámetro (2)
De acuerdo con la salida del compilador, las firmas del método son diferentes en ambos ejemplos (compile el código con la opción -Xlint:unchecked
para confirmarlo):
<X>getSupplier() in A (m2)
1st snippet
getSupplier() in B (m1)
<X>getSuppliers(Collection<String> strings) in A (m2)
2nd snippet
getSuppliers(Collection<String> strings) in B (m1)
De acuerdo con la especificación JLS , la firma de un método m 1 es una subscripción de la firma de un método m 2 si:
m 2 tiene la misma firma que m 1 , o
la firma de m 1 es igual a la eliminación de la firma de m 2 .
La primera declaración está fuera del juego: las firmas de los métodos son diferentes. Pero ¿qué pasa con la segunda afirmación y borrado?
Anulación válida
B.getSupplier()
(m 1 ) es una subscripción de A.<X>getSupplier()
(m 2 ), porque:
- la firma de m 1 es igual a la eliminación de la firma de m 2
<X>getSupplier()
después del borrado es igual a getSupplier()
.
Anulación no válida
B.getSuppliers(...)
(m 1 ) no es una subscripción de A.<X>getSuppliers(...)
(m 2 ), porque:
- la firma de m 1 no es lo mismo que el borrado de la firma de m 2
La firma de m 1 :
getSuppliers(Collection<String> strings);
Borrado de la firma de m 2 :
getSuppliers(Collection strings);
Cambiar el argumento m 1 de la Collection<String>
a la Collection
sin Collection
elimina un error, en este caso m 1 se convierte en una subsignatura de m 2 .
Conclusión
Primer fragmento de código (anulación válida) : las firmas del método en las clases padre e hijo son diferentes inicialmente. Pero, después de aplicar el borrado al método principal, las firmas se vuelven iguales.
Fragmento del segundo código (anulación no válida) : las firmas del método son diferentes inicialmente y siguen siendo diferentes después de aplicar el borrado al método principal.
Me pregunto por qué este es un reemplazo válido:
public abstract class A {
public abstract <X> Supplier<X> getSupplier();
public static class B extends A {
@Override
public Supplier<String> getSupplier() {
return String::new;
}
}
}
Considerando que esto no es:
public abstract class A {
public abstract <X> Supplier<X> getSuppliers(Collection<String> strings);
public static class B extends A {
@Override
public Supplier<String> getSuppliers(Collection<String> strings) {
return String::new;
}
}
}
Según JLS §8.4.8.1 , B.getSupplier
debe ser un A.getSupplier
:
Un método de instancia mC declarado o heredado por la clase C, anula desde C otro método mA declarado en la clase A, si todos los siguientes son verdaderos:
- ...
- La firma de mC es una subscripción (§8.4.2) de la firma de mA.
- ...
Las suscripciones se definen en JLS §8.4.2 :
Dos métodos o constructores, M y N, tienen la misma firma si tienen el mismo nombre, los mismos parámetros de tipo (si los hay) (§8.4.4) y, después de adaptar los tipos de parámetros formales de N a los parámetros de tipo de M, los mismos tipos de parámetros formales.
La firma de un método m1 es una subscripción de la firma de un método m2 si:
- m2 tiene la misma firma que m1, o
- La firma de m1 es la misma que el borrado (§4.6) de la firma de m2.
Así que parece que B.getSupplier
es una A.getSupplier
de A.getSupplier
pero B.getSuppliers
no es una A.getSuppliers
de A.getSuppliers
.
Me pregunto cómo puede ser el caso.
Si B.getSupplier
es una subsignatura de A.getSupplier
porque tiene el mismo borrado, entonces B.getSuppliers
también debe tener el mismo borrado que A.getSuppliers
. Esto debería ser suficiente para que los getSuppliers
sean legales, pero no lo es.
Si B.getSupplier
es una subsignatura de A.getSupplier
porque tiene la misma firma, entonces me pregunto qué significa exactamente "los mismos parámetros de tipo (si los hay)".
Si se consideran los parámetros de tipo, entonces deben tener diferentes parámetros de tipo: A.getSupplier
tiene el parámetro de tipo X
, B.getSupplier
no tiene ninguno.
Si los parámetros de tipo no son considerados, ¿cómo es diferente getSuppliers
?
Esto es más bien una pregunta académica sobre las anulaciones y los genéricos, así que no sugiera un código de refactorización (como mover el parámetro de tipo X
a la clase, etc.).
Estoy buscando una respuesta formal, basada en JLS.
Desde mi punto de vista, B.getSupplier
no debería poder anular A.getSupplier
ya que no tienen los mismos parámetros de tipo. Esto hace que el siguiente código (que produce la ClassCastException
) sea legal:
A b = new B();
URL url = b.<URL>getSupplier().get();
En el momento en que agregó el parámetro, dejó de ser una anulación y se convirtió en una sobrecarga.
Los genéricos no tienen nada que ver con eso.