Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics, how to enforce two arguments of a method that extend a superclass to have a same type?

Tags:

java

generics

Suppose I have a class hierarchy as follow:

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

I have a function which compares the two object

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

At compile time, the method above guarantees the two objects is Vehicle, but it cannot make sure the types of the two object are the same.

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

Can I achive this at compile time using Generics?

P.s: currently, I've implemented it will throw exception if the Class of two objects are not the same. But I'm not satisfied with this.

Thanks in advance.

like image 400
Genzer Avatar asked Jun 19 '12 04:06

Genzer


People also ask

Can generics be used with inheritance in several ways what are they?

Generics also provide type safety (ensuring that an operation is being performed on the right type of data before executing that operation). Hierarchical classifications are allowed by Inheritance. Superclass is a class that is inherited. The subclass is a class that does inherit.

How do you declare a generic bounded type parameter in Java?

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number . Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).


3 Answers

Yes, you can (kind of)!

The type T is being inferred from the arguments, but you can specify the type:

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

Without the typing the method, the type T is inferred to be the narrowest class that satisfies the bounds as used, so for parameters Car and Plane, the narrowest type that will work is Vehicle, so these two lines are equivalent:

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

The above code assumes the generateDiff() is a static method. If it's an instance method, you could type your class and have that type used in your method.

like image 166
Bohemian Avatar answered Jan 22 '23 17:01

Bohemian


Once you get that deep into it, it gets a bit abstract. You would have to provide the class type for the function as well (see below). If you want to enforce such behavior, I would recommend writing separate methods that accept the types you wish to compare. Anyway:

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

And you can use it as such:

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

Not sure why any sane person would want to do this though! :)

like image 34
cklab Avatar answered Jan 22 '23 17:01

cklab


It is not possible in my opinion, any method which can accept Vehicle as an argument will be able to accept CAR and PLANE. Since compiler dont know what kind of Objects will come(can be CAR, BUS, PLANE), it cannot guarentee that two params are of exactly same type. If some one extends CAR and creates a FORD? both objects are type CAR.

Only way to ensure this is at runtime using custom logic.

like image 36
Subin Sebastian Avatar answered Jan 22 '23 15:01

Subin Sebastian