Reglas de herencia y anulación del método genérico Java
-
27-10-2019 - |
Pregunta
Tengo una clase abstracta que tiene un método genérico y quiero anular el método genérico sustituyendo tipos específicos por el parámetro genérico. Entonces, en pseudo-código tengo lo siguiente:
public abstract class GetAndParse {
public SomeClass var;
public abstract <T extends AnotherClass> void getAndParse(T... args);
}
public class Implementor extends GetAndParse {
// some field declarations
// some method declarations
@Override
public <SpecificClass> void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
¿Pero por alguna razón no se me permite hacer esto? ¿Estoy cometiendo algún tipo de error de sintaxis o este tipo de herencia y primordial no está permitido? Específicamente recibo un error sobre @Override
Porque el IDE de Eclipse me recuerda a implementar getAndParse
.
Así es como quiero que funcione el código anterior. En otro lugar de mi código hay un método que espera instancias de objetos que implementen GetAndParse
que específicamente significa que tienen un getAndParse
método que puedo usar. Cuando llamo getAndParse
En ese caso, el compilador verifica si he usado instancias específicas de T
de la manera adecuada, así que en particular T
Debería extenderse AnotherClass
y debería ser SpecificClass
.
Solución
Lo que tenemos aquí son dos métodos diferentes con parámetros de tipo individual cada uno.
public abstract <T extends AnotherClass> void getAndParse(Args... args);
Este es un método con un parámetro de tipo llamado T, y limitado por AnotherClass
, lo que significa cada subtipo de AnotherClass
está permitido como un parámetro de tipo.
public <SpecificClass> void getAndParse(Args... args)
Este es un método con un parámetro de tipo nombrado SpecificClass
, delimitada por Object
(lo que significa que cada tipo está permitido como un parámetro de tipo). ¿De verdad quieres esto?
Es el parámetro de tipo utilizado dentro Args
? Creo que el problema estaría ahí.
El significado de
public abstract <T extends AnotherClass> void getAndParse(T... args);
es que el llamador del método puede decidir con qué parámetro de tipo quiere llamar al método, siempre y cuando sea un subtipo de AnotherClass
. Esto significa que, en efecto, el método se puede llamar con cualquier objeto de tipo AnotherClass
.
Dado que la persona que llama puede decidir el parámetro de tipo, no puede en una subclase reducir el tipo de parámetro a SpecificClass
- Esto no sería una implementación del método, sino otro método con el mismo nombre (sobrecarga).
Quizás quieras algo como esto:
public abstract class GetAndParse<T extends AnotherClass> {
public SomeClass var;
public abstract void getAndParse(T... args);
}
public class Implementor extends GetAndParse<SpecificClass> {
// some field declarations
// some method declarations
@Override
public void getAndParse(SpecificClass... args) {
// method body making use of args
}
}
Ahora el getAndParse
El método implementa el método de la clase principal.
Otros consejos
Estás viendo este problema debido al concepto llamado "borrado" en Java Generics. Java usa "borrado" para admitir la compatibilidad con atraso. es decir, código Java que no usaba genéricos.
Procedimiento de borrado:
El compilador primero hará una verificación de tipo y luego eliminará (borrará) todos los parámetros de tipo tanto como sea posible, y también insertará el typecasting cuando sea necesario.
ejemplo:
public abstract <T extends AnotherClass> void getAndParse(T paramAnotherClass);
se convertirá
public abstract void getAndParse(AnotherClass paramAnotherClass);
En clase "implementer.java",
El código
public <SpecificClass> void getAndParse(T paramAnotherClass)
se convertirá
public void getAndParse(SpecificClass paramAnotherClass){ }
El compilador verá que no ha implementado el método abstracto correctamente. Existe un tipo de falta de coincidencia entre el método abstracto y el método implementado. Por eso está viendo el error.
Más detalles se pueden encontrar aquí.http://today.java.net/pub/a/today/2003/12/02/explorations.html
No, no es válido. ¿Qué pasaría si alguien con un GetAndParse
referencia lo llamó con una diferente Extensión de la clase AnotherClass
?
Eso se convierte en una tontería cuando alguien tiene una referencia al tipo GetAndParse e intenta llamar al método GetAndParse. Si el gato y el perro extienden otra clase. Debería esperar poder llamar a GetAndParse#GetAndParse con un gato o un perro. ¡Pero la implementación ha tratado de restringirlo y hacerlo menos compatible!
No puedes anular al tipo específico T porque de hecho hay (en el nivel de bytecodo si lo desea) solo un método getAndParse
Debido al tipo de borrado (ver otra respuesta):
public abstract void getAndParse(AnotherClass... args); // (1)
Para cada tipo de T, se utiliza el mismo método.
Puedes sobrecarga Es (creo):
public void getAndParse(SpecificClass... args); // (2)
pero este no será un método diferente de (1) hormiga lo hará no ser llamado por código genérico:
T x = whatever;
object.getAndParse(x); // Calls (1) even if T is derived from SpecificClass
El método estático no puede anular
class Vehicle{
static void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
@Override //error
void park(int location) { //error
System.out.println("Car Parking..");
}}
El método privado no puede anular
class Vehicle{
private void park(int location){
System.out.println("Vehicle parking..");
}
void callPark(){
park(100);
}}
class Car extends Vehicle{
//@Override
void park(int location) {
System.out.println("Car Parking..");
}}
class Demo {
public static void main(String[] args) {
Vehicle v1=new Car();
v1.callPark();
}}
El método final no puede anular
class Vehicle{
final void park(int location){
System.out.println("Vehicle parking..");
}}
class Car extends Vehicle{
//@Override
void park(int location) { //error
System.out.println("Car Parking..");
}}