简体   繁体   中英

Java not allowed to call recursive method with generic type?

public class ResourceAssembler<T extends BasedEntity> {


    public Resource<T> toResource(T  entity) {
       ExtendsBasedEntity e = getExtendsBasedEntity();
       toResource(e); //<----compile error
       //some other code
    }
}

public class ExtendsBasedEntity extends BasedEntity{}

But if you call it from the outside its fine

//some other class
new ResourceAssembler<ExtendsBasedEntity>().toResource(new ExtendsBasedEntity())

Why?

Error:(28, 25) java: incompatible types: spring.BasedEntity cannot be converted to T

T may not be ExtendsBasedEntity , but some other subtype of BaseEntity , hence the compile error.

One way to "fix" the problem, is to use a type token

public class ResourceAssembler<T extends BasedEntity> {

    private final Class<T> type;

    public ResourceAssembler(Class<T> type) {
        this.type = type;
    }

    public Resource<T> toResource(T  entity) {
       toResource(type.newInstance());
       //some other code
    }
}

Assuming that works for you.

Let's create two classes extending BasedEntity and call them EBE1 and EBE2 .
Now you create a ResourceAssembly object using EBE1 as the type parameter. But let's say that in the implementation of the toResource method you do something like return toResource(new EBE2()); .

So the return type of toResource() is becoming Resource<EBE2> but that is wrong because according to the structure you should return Resource<EBE1> . And that's why the compile time error. And type safety instincts of Java kicks in.

If you want to do return a generic for the toResource method then you either have to pass in the entity object down as it is or change it to the concrete type that you are initializing it within and not use generic (although I don't know why would anyone use the second option, but it's a "solution" to make it "compile").

Also, on the outside when you declare it. You are not specifying the type parameter for ResourceAssembly and hence it's a raw one. Try to do it with a type param. You will have red squiggly lines there as well.

Here is an example:

static class Resource<T> {

}

static class BasedEntity {

}

static class ExtendsBasedEntity1 extends BasedEntity {

}

static class ExtendsBasedEntity2 extends BasedEntity {

}
static public class ResourceAssembler<T extends BasedEntity> {


    public Resource<T> toResource(T entity) {
        return toResource(new ExtendsBasedEntity1()); //<----compile error
    }
}  



    public static void main(String[] args) {
        new ResourceAssembler<ExtendsBasedEntity1>().toResource(new ExtendsBasedEntity1()); // <---- No errors or warnings. This is valid and legal
        new ResourceAssembler<ExtendsBasedEntity2>().toResource(new ExtendsBasedEntity1()); // <----- red squiggly lines here
        new ResourceAssembler().toResource(new ExtendsBasedEntity2()); // <--compiler warning about raw types but no error
    }  

If you anyhow need to make it work the way you want it to, then instead of returning Resource<T> , return Resource<ExtendsBasedEntity> because you are recursing inside a generic method and looks like you need an object of concrete type to go in as the parameter for the recursive call. So it would make sense to do so.
Or else, go with @Bohemian's approach and make sure that in the class declaration of the type that you are using, there is a no-args constructor or else you will be having InstantiationException .

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