Home > Blockchain >  Unexpected behaviour with generics
Unexpected behaviour with generics

Time:07-08

I'm facing this error while work with Java generics, and I don't understand which is the problem;
I have two classes like these:

public abstract class BaseClass{
   ....
}

public class OneClass extends BaseClass{
   ....
}

I have a generic repo for OneClass:

public class MyRepo<T extends BaseClass>{
   List<T> getElements();
}

Then I have a method that should works whit generics:

private MyRepo<OneClass> myRepo;   

public <T extends BaseClass> List<T> myMethod(){
     return myRepo.getElements();
}

The method doesn't work unless I force a cast to List ( as shown below ):

public <T extends BaseClass> List<T> myMethod(){
   return  (List<T>)  myRepo.getElements();
}

I don't understand which is the problem given that OneClass extends BaseClass.
Thanks for any suggestion.

CodePudding user response:

Error message tells you that not every T extends BaseClass is OneClass

CodePudding user response:

You should make sure that the field myRepo is the same type as T as in your method. If you force it to be OneClass you cant use othere types except OneClass. So there is no use of a generic. If you want to allow every extending class from BaseClass you could make the class of the mehtod generic in oder to use the same type of T as shown below:

public class FunctionClass<T extends BaseClass> {
    private MyRepo<T> myRepo; 
        
    public List<T> myMethod(){
        return  myRepo.getElements();
    }
}

CodePudding user response:

Having a method of the form:

<T> T myMethod()

makes the inference of the actual T dependent on the call-site:

String s = myMethod();
Integer i = myMethod();

Considering your scenario one could invoke your method like this:

List<BaseClass> a = myMethod();
List<OneClass> a = myMethod();

As you can see this can be incorrect as myMethod could actually return another subtype of BaseClass (lets say TwoClass) which is not correct to cast to List<OneClass> - thus you need the unsafe cast to List<T>.

You should change the signature of myMethod to one of the following:

public List<? extends BaseClass> myMethod(){}
public List<BaseClass> myMethod(){}

The first variant states that this is a list of any subtype of BaseClass the other just omits that information.

Dependent on what you want to achieve check the other answer or read about PECS (Producer Extends, Consumer Super) or f-bounded polymorphism / self-types to return the concrete type.

  • Related