Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics & incompatible types using <S extends {class}>

Tags:

java

generics

abstract class AbsClass {}

class MyAbs extends AbsClass {}

class MyClass<S extends AbsClass> {
    S getObj() {
        return new MyAbs();
    }
}

Getting compiler issue:

Error:(33, 16) java: incompatible types: MyAbs cannot be converted to S

What is the correct way to do this?

Edit: I was hoping to be able to intialize MyClass{MyAbs} then call getObj() which would return me a MyAbs object. With Andy's answer, I would have to cast the AbsClass to MyAbs or MySecondAbs which was what I was trying to avoid

like image 722
mwong56 Avatar asked Nov 09 '16 21:11

mwong56


2 Answers

Andy has described how to fix the problem, I'll try to explain why.

S is not guaranteed to be assignable to MyAbs, only to AbsClass, and each instance will specify a subclass of AbsClass.

Consider:

class MyOtherAbs extends AbsClass {}

MyClass myClass = new MyClass<MyOtherAbsClass>{};

This would conflict with what you have.

UPDATE:

Based on your comments it looks like you want to achieve the above. The challenge is what if MyOtherAbs has a constructor with arguments? Or is implemented as a Singleton (i.e. a private constructor and static getInstance()). In short, there is no guaranteed unified way you can construct these. Further, due to type erasure, at runtime, the concept of S is gone, so you cannot do something like:

return new S();

You basically have two alternatives, one is to use subclassing:

public interface AbsClassFactory<S extends AbsClass>{ //takes the place of MyClass
    public S getInstance();
}

MyAbsFactory implements AbsClassFactory<MyAbs>{
    public MyAbs getInstance(){
        return new MyAbs(); //or however MyAbs is instantiated
    }
}

The other option is reflection:

class MyClass {
    public <S extends AbsClass> S getObj(Class<S> clazz) throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}

Note that the second assumes there is a no arg constructor available and will throw a runtime exception if one is not.

like image 139
Taylor Avatar answered Oct 17 '22 10:10

Taylor


S extends MyAbs, not the other way around. any object of type S can be cast into MyAbs, but MyAbs cannot be cast into it's derived classes.

Please explain what you are trying to achieve.

like image 3
Shloim Avatar answered Oct 17 '22 12:10

Shloim