Java compiler not able to infer type on generic chain

≯℡__Kan透↙ 提交于 2020-01-24 07:57:05

问题


As mentioned below (to remark it as I might have explained myself badly):

I want to understand the principle behind this issue so that I can apply that knowledge to the real problem.


ISSUE START

I am working in a system intended to be an abstract library to be used by many subsystems. The idea is to have an standard behaviour extensible by implementations. My problem is that java compiler is not able to inferr method parameter type, even though it makes no sense since the boundaries are well set on every related generic class/method.

Below there is an example, the minimum example to reproduce the problem. I am aware it looks a bit silly in this example, but that is because of the simplification:

    public class Test {
    public static void main(String[] args) {
        Gen<? extends Base> gen = new HijaGen();
        gen.applyTo(Hija.EXAMPLE);
    }
    private interface Base {
        String getName();
    }
    private enum Hija implements Base {
        EXAMPLE;
        @Override
        public String getName() {
            return this.name();
        }
    }
    private interface Gen<T extends Base> {
        boolean applyTo(T base);
    }
    private static class HijaGen implements Gen<Hija> {
        @Override
        public boolean applyTo(Hija base) {
            return false;
        }
    }
}

Basically it says that applyTo expects a subclass of Base and Hija is not valid, which from my perspective makes no sense.

Thanks in advance.

EDIT:

This code is for a library, so that the solution could not be specifying the type since then it could not be extensible through particular implementations.

I am already aware that if I specify the generic type instead of throwing a type wildcard it will perfectly work. But my question is how is it possible that even though Hija subclasses Base, and method firm requires a subclass of Base it will never compile...

I want to understand the principle behind this issue so that I can apply that knowledge to the real problem.


回答1:


I think there is a similar problem explained in the docs here and the problem that they propose is to make a helper (or wrapper) to clarify the types. So I guess you can try adding this function:

private static <T extends Base> boolean applyTo(Gen<T> gen, T base){
    return gen.applyTo(base);
}

And change the main function as follows:

public static void main(String[] args) {
    boolean b = applyTo(new HijaGen(), Hija.EXAMPLE);
}



回答2:


A Gen<? extends Base> is a Gen of something which extends Base. It applies to some specific type of Base but we do not know which. In practice, this means you will never be able to call applyTo on a variable with type Gen<? extends Base>, except by passing null.

Change your code to

Gen<Hija> gen = new HijaGen();
gen.applyTo(Hija.EXAMPLE);

I suspect you'll probably say you can't do that, because your code is just an example, and the above is not possible in the real code. In which case you will need to give a better example




回答3:


On the lines of what Carlos has mentioned, there is one more way to resolve this. It can be used if you are sure of the suppliers of the implementation of Gen<T>.

Here, instead of the helper to call applyTo(), we define a factory method to create the instance of the Gen implementation and cast it to Gen<Base>, which in my opinion is safe for all practical purposes. Note that the factory method get() need not be static. The rest of the code remains unchanged from your sample.

public static void main( String[] args ){
    Gen<Base> gen = (Gen<Base>) get();
    gen.applyTo( Hija.EXAMPLE );
}

static Gen<? extends Base> get(){
    /* Create the instance of the Gen interface implementation here. */
    return new HijaGen();
}

Explanation

Gen<? extends Base> expects to refer to an instance of an implementation of Gen that uses a type that is either Base or an sub-type of it. Since a "sub-type" can be from one of many possible hierarchies from Base downward (as shown in the picture below), it cannot be sure that the parameter passed to applyTo() method is of the same child hierarchy path and not just a 'relative' with the same ancestral parent Base. That is why it won't allow a call to applyTo() with its reference being of Gen<? extends Base> for a parameter of with reference as Base.

However, when the reference is Gen<Base>, it knows that applyTo() will accept any parameter that is a child type of Base. And hence, it stops worrying about type mismatch.

I have tried to show what I mean by different child hierarchy paths. It is like the case of a family tree.



来源:https://stackoverflow.com/questions/59715598/java-compiler-not-able-to-infer-type-on-generic-chain

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!