简体   繁体   中英

Java Generic method takes a parameter of the class's type

I have the following code:

public class Animal {

    public <T extends Animal> void doSomething(T instance) {
       //do stuff
    }

}

but the parameter to the method should be of the same type as the class it's being called on. Eg this is valid:

public class Dog extends Animal {} 
public class Cat extends Animal {} 

Dog dog1 = new Dog();
Dog anotherDog = new Dog();
dog1.doSomething(anotherDog);

But this is NOT valid:

dog1.doSomething(new Cat());

Is there any way to achieve this without implementing the method on all the subclasses? Do don't want to do that because there are a lot of subclasses and it would be too repetitive.

Generics serve to link things together. They tell the compiler that relationships exist between 2 different places that types occur. However, in your signature, T occurs only once: As parameter. This is useless. Why not just write void doSomething(Animal instance) ? Your code is just a more complicated, and less flexible way of writing that. After all, any animal, even an instance of a subclass of animal, is an animal . There is nothing you can pass to the method <T extends Animal> void doSomething(T animal) that you cannot pass to void doSomething(Animal animal) !

What you want is both not possible and non-sensical. Imagine it was possible to tell java that the parameter of doSomething must be of the same type as the receiver of doSomething , then, consider this code:

Dog dog = new Dog();
Cat cat = new Cat();
Animal dogA = dog; // dogA and dog are pointing at the exact same object!
Animal catA = cat; // dogA and dog are pointing at the exact same object!
dogA.doSomething(catA); // so this would compile and run, then.

Look at that last line. No amount of type-based insistence that 'the parameter must be of the same type as the receiver' you care to add to java can ever make the compiler refuse to compile this code. And yet there is no difference between that and dog.doSomething(cat) , because dog and dogA as well as cat and catA are referring to the same object.

And yet, that is evidently what you want. Thus, what you want is not a sensible thing to want.

There are things you can do, but this almost always leads to more problems. You have to generify your own type:

public class Animal<S extends Animal<S>> {
    public void doToSibling(S other) {}
}

But this requires you to declare: class Dog extends Animal<Dog> , and there is nothing actually stopping you from class Dog extends Animal<Cat> . The S is not so much 'self type' as 'the thing I can do doToSibling to'.

I don't think there is an implicit way to reference to an overriding class.

You can declare the reqired subtype as generic argument on animal itself to have the described effect.

public class Animal<T extends Animal<?>> {

    public void doSomething(final T instance) {
       //do stuff
    }

}


public class Dog extends Animal<Dog> {}
public class Cat extends Animal<Cat> {}

That beeing said, in practice it is hard to imagine where you want to go from here. What could a method contain that only two dogs can do but two different animals can't but also can be implemented with the same code?

This is often the point where you want to overthink if a common abstract base-class really is helpful in the long run. If the implementation code turns out to be different for all the animals, maybe a common interface on independent classes might be the better option.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM