2012-06-19 12 views
9

Supongamos que tengo una jerarquía de clases de la siguiente manera:Java Generics, cómo aplicar dos argumentos de un método que extiende una superclase para tener el mismo tipo?

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

I tienen una función que compara los dos objeto

public <T extends Vehicle> generateDiff(T original, T copy) 

En tiempo de compilación, el método anterior garantiza los dos objetos es Vehicle, pero no puedo asegurarme de que los tipos de los dos objetos sean iguales.

generateDiff(new Car(), new Car()); //OK 
generateDiff(new Plane(), new Plane()); //OK 
generateDiff(new Car(), new Plane()); //WRONG 

¿Se puede lograr esto en tiempo de compilación usando Generics?

P.s: actualmente, lo he implementado arrojará una excepción si el Class de dos objetos no es el mismo. Pero no estoy satisfecho con esto.

Gracias de antemano.

Respuesta

8

Sí, puedes (tipo de)!

El tipo T es ser inferred de los argumentos, pero se puede especificar el tipo:

MyClass.<Car>generateDiff(new Car(), new Plane()); // generates a compile error 

Sin el escribiendo el método, el tipo T se infiere que la clase más estrecho que satisface los límites y usadas , por lo que para los parámetros Car y Plane, el tipo más estrecho que va a trabajar es Vehicle, por lo que estas dos líneas son equivalentes:

generateDiff(new Car(), new Plane()); // type is inferred as Vehicle 
MyClass.<Vehicle>generateDiff(new Car(), new Plane()); 

El código anterior asume que generateDiff() es un método estático. Si se trata de un método de instancia, puede escribir su clase y usar ese tipo en su método.

+0

+1 para inferir el tipo! – UmNyobe

3

No es posible, en mi opinión, que cualquier método que acepte Vehicle como argumento pueda aceptar CAR y PLANE. Como el compilador no sabe qué tipo de Objetos vendrá (puede ser CAR, BUS, PLANE), no puede garantizar que dos params sean exactamente del mismo tipo. Si alguien extiende CAR y crea un FORD? ambos objetos son tipo CAR.

Única forma de garantizar esto en tiempo de ejecución mediante la lógica personalizada.

1

No, esto no se puede lograr con o sin genéricos. Básicamente, estás preguntando si puedes decirle al compilador que infrinja las reglas del polimorfismo.

Incluso si definió explícitamente un método sin genéricos, como el que se muestra a continuación, aún aceptaría cualquier par de clases que extiendan Vehicle.

void generateDiff(Vehicle maybePlane, Vehicle maybeCar) { ... 

No es un escenario que es una excepción, pero yo no lo recomendaría. Si llama al método contra objetos de clase final (o cualquier clase que no se extienda) que extienda Vehicle, puede anular el método para que coincida con la firma de esa clase para cada parámetro. Pero necesita definir cada uno de forma explícita.

class Vehicle; 
final class Car extends Vehicle; 
final class Plane extends Vehicle; 

void generateDiff(Car car1, Car car2) { ... 
void generateDiff(Plane plane1, Plane plane2) { ... 

generateDiff(new Car(), new Car()); // OK 
generateDiff(new Plane(), new Plane()); // OK 
generateDiff(new Car(), new Plane()); // No matching method 
1

¿Puedo achive esta en tiempo de compilación usando los genéricos?

Nop debido a que los tipos se fijan en tiempo de ejecución, pero se puede simplemente utilizar la introspección y una excepción si un tipo no válido se envía a la función, aunque si ese es el caso, entonces tal vez theres un defecto en su diseño , Pero esa es solo mi opinión.

instanceof operador comprobará cuál es el tipo subyacente, por lo que puede hacer una excepción o hacer algo más apropiado.

public <T extends Vehicle> generateDiff(T original, T copy) 

ya que es posible que la comparación de diferentes tipos, tal vez usted no debería tener una sola función, ya que requeriría una buena cantidad de if else puede ser más sensato para implementar la función correspondiente en cada clase para que puedan adecuadamente comparar con objetos del tipo apropiado, aunque estoy asumiendo una cantidad justa de suposiciones, lo que puede ser incorrecto.

6

Una vez que se profundiza en eso, se vuelve un poco abstracto. También debe proporcionar el tipo de clase para la función (ver a continuación). Si desea imponer tal comportamiento, le recomendaría escribir métodos separados que acepten los tipos que desea comparar. De todos modos:

public <C extends Vehicle> void generateDiff(Class<C> type, C original, C copy); 

Y puede utilizar como tal:

generateDiff(Plane.class, new Plane(), new Plane()); // OK 
generateDiff(Car.class, new Car(), new Car()); // OK 
generateDiff(Plane.class, new Plane(), new Car()); // ERROR 
generateDiff(Vehicle.class, new Plane(), new Car()); // OK 

No sé por qué cualquier persona en su sano juicio querría hacer esto, sin embargo! :)

+0

+1 por '¡No estoy seguro de por qué cualquier persona en su sano juicio querría hacer esto! :) ' –

0

¡No, eso no es posible!

Esto es lo que haría:

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

class DiffGenerator { 
    public Diff generateDiff(Car original, Car copy) { 
    return generateDiff(original, copy) 
    } 

    public Diff generateDiff(Plane original, Plane copy) { 
    return generateDiff(original, copy) 
    } 

    private Diff generateDiff(Vehicle original, Vehicle copy) { 
    return generatedDiff; 
    } 

} 

notado la método privado? private Diff generateDiff(Vehicle original, Vehicle copy)

3

Estrictamente hablando, la respuesta a su pregunta es 'no, no es posible'.

Sin embargo, hay un workaround. Cree un método <T extends Vehicle> VehicleDiffer<T> compare(T vehicleA) donde VehicleDiffer<T> tiene un método ReturnType with(T vehicleB). Ahora usted puede hacer las llamadas siguientes:

compare(new Car()).with(new Car()); // okay 
compare(new Plane()).with(new Plane()); // okay 

El siguiente fallará:

compare(new Car()).with(new Plane()); // with(Car) can't be called with argument type Plane 
+0

Idea genial que digo. – Saintali

Cuestiones relacionadas