简体   繁体   English

Java泛型无法将Type转换为Type

[英]Java Generics cannot convert Type in Type

I'm having trouble with this: 我对此有麻烦:

public class Test {

    static class TestType {}

    static class Pair<A,B>{}

    public static void main( String [] args ) throws Exception {

        Collection<? extends TestType> val = null;

        List<? extends TestType> single = 
            testSingle( val ); // OK

        Pair<String,List<? extends TestType>> pair = 
            testPair( val ); // ERROR

    }

    static <T extends TestType> List<T> testSingle( Collection<T> val ){
        return null;
    }

    static <T extends TestType> Pair<String,List<T>> testPair( Collection<T> val ){
        return null;
    }

}

Why is the first one working and the second one isn't? 为什么第一个无效,而第二个无效?

The Error message ist: 错误消息:

Type mismatch: cannot convert from Test.Pair<String,List<capture#3-of ? extends Test.TestType>> to Test.Pair<String,List<? extends Test.TestType>>

EDIT: 编辑:

using Braj's Answer I setup this to clearify the Problem when not using generic type T: 使用Braj的答案,我在不使用泛型T时设置了此问题,以解决问题:

public class Test {

    static class TestType {}
    static class Implementation extends TestType {}

    static class Pair<A,B>{}

    public static void main( String [] args ) throws Exception {

        Collection<Implementation> val = null;

        List<Implementation> sinle = testSingle( val ); // OK

        Pair<String,List<Implementation>> pair = testPair( val ); // OK
        Pair<String,List<Implementation>> pair2 = testPair2( val ); // ERROR
        Pair<String,List<Implementation>> pair3 = testPair3( val ); // ERROR
        Pair<String,? extends List<Implementation>> pair4 = testPair4( val ); // ERROR

        run( val );

    }

    private static void run( Collection<? extends TestType> val ){

        List<? extends TestType> single = testSingle( val ); // OK

        Pair<String,List<? extends TestType>> pair = testPair( val ); // ERROR
        Pair<String,List<? extends TestType>> pair2 = testPair2( val ); // ERROR
        Pair<String,List<? extends TestType>> pair3 = testPair3( val ); // OK
        Pair<String,? extends List<? extends TestType>> pair4 = testPair4( val ); // OK
        Pair<String,? extends List<? extends TestType>> pairX = testPair( val ); // OK
        //My-Name-Is
        @SuppressWarnings( "unchecked" )
        Pair<String,List<? extends TestType>> fixed = 
            (Pair<String,List<? extends TestType>>)
            (Pair<String,?>) testPair( val ); // OK but ugly and stupid(?)
    }

    private static <T extends TestType> List<T> testSingle( Collection<T> val ){
        return null;
    }

    private static <T extends TestType> Pair<String,List<T>> testPair( Collection<T> val ){
        return null;
    }

    // Braj1
    private static <T extends TestType> Pair<String, List<TestType>> testPair2(Collection<T> val) {
        return null;
    }

    // Braj2
    private static <T extends TestType> Pair<String, List<? extends TestType>> testPair3(Collection<T> val) {
        return null;
    }

    // Seelenvirtuose
    private static <T extends TestType> Pair<String, ? extends List<? extends TestType>> testPair4(Collection<T> val) {
        return null;
    }

    // This one works in the way I wanted.
    private static <T extends TestType> void runOK( Collection<T> val ){

        List<T> single = testSingle( val ); // OK

        Pair<String,List<T>> pair = testPair( val ); // OK
    }
}

EDIT2: I can fix this in run() using: EDIT2:我可以使用以下方法在run()中解决此问题:

@SuppressWarnings( "unchecked" )
Pair<String,List<? extends TestType>> fixed = 
    (Pair<String,List<? extends TestType>>)
    (Pair<String,?>) testPair( val );

But this is rather ugly and stupid (?). 但这是丑陋且愚蠢的(?)。

EDIT3: I edited the above to include Seelenviruose's answer and it's still getting weirder. EDIT3:我对上面的内容进行了编辑,以包含Seelenviruose的答案,但它仍然变得越来越奇怪。

I still don't know why this is needed... 我仍然不知道为什么需要这个...

EDIT4: Finally got it working without ugly casts: if I use <T extends TestType> run(...) the compiler doesn't complain. EDIT4:终于在没有难看的强制转换的情况下运行了它:如果我使用<T extends TestType> run(...) ,编译器不会抱怨。 I changed it above. 我在上面改了

The return type of your second method call is 您的第二个方法调用的返回类型是

Pair<String,List<? extends TestType>>

Why is that the return type? 为什么是返回类型? Well, the compiler infers that type from the input argument val that is of type Collection<? extends TestType> 好吧,编译器从类型为Collection<? extends TestType>的输入参数val推断出该类型Collection<? extends TestType> Collection<? extends TestType> . Collection<? extends TestType>

The upper bounded wildcards are the Java mechanism to break generic's inherent invariance. 上限通配符是打破通用的固有不变性的Java机制。 With such a construct the following is possible: 通过这种构造,可能实现以下目的:

Collection<? extends Number> c1 = new LinkedList<Integer>();
Collection<? extends Number> c2 = new HashSet<Double>();

Note, that I introduced two kinds of polymorphism. 注意,我介绍了两种多态性。 One for the collection type, and another for the type variable. 一个用于集合类型,另一个用于类型变量。

Without the upper bounded wildcard the following construct is be a compiler error: 如果没有上限通配符,则以下构造是编译器错误:

Collection<Number> c3 = new LinkedList<Integer>(); // compiler error

Why the error? 为什么会出错? Simple: If you have a Collection<Number> you would expect to being able to add a Double object into it. 简单:如果您有Collection<Number> ,则希望能够向其中添加Double对象。 But this would not be allowed into a LinkedList<Integer> . 但这不会被允许到LinkedList<Integer> Hence, error. 因此,错误。

The downside of this upper bounded wildcard is now, that you can't add anything (except null ) into a Collection<? extends Number> 现在,此上限通配符的缺点是,您不能将任何东西( null除外)添加到Collection<? extends Number> Collection<? extends Number> . Collection<? extends Number>


Cut: If you understood it until now, the rest should be easy. 切:如果您到目前为止还不了解,剩下的应该很简单。


You now introduced an additional layer into the generic return type. 现在,您在通用返回类型中引入了一个附加层。 You do not return a list of something, you return a pair of a string and some list . 您不返回某些内容的列表,而是返回一对字符串和一些list

So the upper bound now must not be on the type T of your list elements. 因此,上限现在不能位于列表元素的类型T上。 It must be placed onto the list itself! 它必须放在列表本身上!

With the following method declaration 用以下方法声明

<T extends TestType> Pair<String, ? extends List<T>> testPair(Collection<T> val) { ... }

the following call will be allowed: 以下通话将被允许:

Pair<String, ? extends List<? extends TestType>> pair = testPair(val);

But ... I doubt that anything reasonable is possible with such a pair. 但是...我怀疑这样的组合是否有任何合理的可能性。 If so, you must reconsider your design. 如果是这样,则必须重新考虑您的设计。

Why is the first one working and the second one isn't? 为什么第一个无效,而第二个无效?

Because List<ASpecificSubTypeOfTestType> (the return of the first method) is a subtype of List<? extends TestType> 因为List<ASpecificSubTypeOfTestType> (第一个方法的返回)是List<? extends TestType>的子类型List<? extends TestType> List<? extends TestType> , but Pair<String, List<ASpecificSubTypeOfTestType>> (the return of the second method) is not a subtype of Pair<String, List<? extends TestType>> List<? extends TestType> ,但是Pair<String, List<ASpecificSubTypeOfTestType>> (第二个方法的返回)不是Pair<String, List<? extends TestType>>的子类型Pair<String, List<? extends TestType>> Pair<String, List<? extends TestType>> . Pair<String, List<? extends TestType>>

Let us change your example from Pair to List , and TestType to Object for a moment: 让我们TestType将示例从Pair更改为List ,将TestTypeObject

public class Test {

    public static void main( String [] args ) throws Exception {

        Collection<?> val = null;

        List<?> single = testSingle( val ); // OK

        List<List<?>> pair = testList( val ); // ERROR

    }

    static <T> List<T> testSingle( Collection<T> val ){
        return null;
    }

    static <T> List<List<T>> testList( Collection<T> val ){
        return null;
    }

}

From a technical point of view, List<SomeSpecificType> is a subtype of List<?> , but List<List<SomeSpecificType>> is not a subtype of List<List<?>> , for the same reason that List<String> is not a subtype of List<Object> -- the type parameters are different concrete types (one is List<SomeSpecificType> and the other is List<?> ). 从技术角度来看, List<SomeSpecificType>List<?>的子类型,但是List<List<SomeSpecificType>>不是List<List<?>>的子类型,出于与List<String>相同的原因。不是List<Object>的子类型-类型参数是不同的具体类型(一个是List<SomeSpecificType> ,另一个是List<?> )。

For a more practical reasoning, testList returns a List<List<T>> for some T . 有关更实用的推理, testList返回一个List<List<T>>对于一些T We don't know what that T is, but we know that it is some concrete type . 我们不知道T是什么,但是我们知道它是某种具体类型 And this List<List<T>> that is returned is a list that can only contain List<T> . 并且返回的这个List<List<T>>是一个只能包含List<T> It cannot contain List<S> if S is not T, because List<S> and List<T> are not subtypes of each other. 如果S不是T,则它不能包含List<S> ,因为List<S>List<T>不是彼此的子类型。 Even though we don't know what T is, we know that there exists some choice of T, and all the element lists must have that T as the type parameter. 即使我们不知道T是什么,我们也知道存在T的选择,并且所有元素列表都必须将T作为类型参数。

On the other hand, the type you're assigning it to, List<List<?>> , is a list that can contain every type of list at the same time. 另一方面,您要为其分配的类型List<List<?>>是一个列表,可以同时包含每种类型的列表。 So you can put in a List<Integer> and a List<String> , etc. at the same time and it would be okay. 因此,您可以同时放入List<Integer>List<String>等,这样就可以了。 You can never do that with a List<List<T>> , no matter what you choose T to be. 无论选择哪种T ,都永远无法使用List<List<T>>做到这一点。

Therefore, the two types are clearly incompatible. 因此,这两种类型显然不兼容。

What can go wrong? 有什么问题吗? With the List<List<?>> reference, you can insert List<Integer> and List<String> into the list. 使用List<List<?>>引用,可以将List<Integer>List<String>插入列表。 And then with the List<List<T>> reference inside the function, you can extract all the elements as List<T> , which they won't be. 然后,使用函数内部的List<List<T>>引用,您可以将所有元素提取为List<T> ,而这些元素将不是。 So it is unsafe. 因此,这是不安全的。

You might say, what if I never put things into the List<List<?>> reference? 您可能会说,如果我从不把东西放到List<List<?>>引用中怎么办? Is that safe? 这样安全吗? But if that were the case, you should instead use List<? extends List<?>> 但是,在这种情况下,您应该使用List<? extends List<?>> List<? extends List<?>> . List<? extends List<?>> The ? extends ? extends ? extends wildcard makes it a consumer, so you cannot insert anything into it except null ; ? extends通配符使它成为使用者,因此您不能在其中插入任何东西( null除外); and it also makes the type compatible ( List<List<SomeSpecificType>> is a subtype of List<? extends List<?>> ). 并且还使类型兼容( List<List<SomeSpecificType>>List<? extends List<?>>的子类型List<? extends List<?>> )。

The moral of the story is, wildcards in deeper parameters do not mean what you think they do. 这个故事的寓意是,更深层的参数中的通配符并不意味着您认为它们会做什么。

Try 尝试

private static <T extends TestType> Pair<String, List<TestType>> testPair(Collection<T> val) {...}

Pair<String, List<TestType>> pair =  testPair(val);

OR 要么

private static <T extends TestType> Pair<String, List<? extends TestType>> testPair(Collection<T> val) {...}

Pair<String, List<? extends TestType>> pair =  testPair(val);

I read through the answers and they explain why it doesn't work but I didn't notice any offers for how to solve it. 我通读了答案,他们解释了为什么它不起作用,但是我没有注意到有关如何解决它的任何提议。 I solved it in my situation by making the method a generic method. 我通过使该方法成为通用方法解决了我的情况。

public main(){    
  Map<String, List<Integer>> stuff = service.buildStuff();
  consumeStuff(stuff);
}

private <T extends Number> consumeStuff(Map<String, List<T>> stuff){
  ...
}

So the method consumeStuff() expects the 2nd type of the map to be a List which has a type of something that extends Number (so Integer or Double). 因此,方法ConsumerStuff()希望地图的第二种类型为List,其类型可以扩展Number(即Integer或Double)。 By having a map (the var stuff inside main()) with the explicit type of Integer for the List means that Java can infer the type when calling the generic method consumeStuff(). 通过使用一个具有List的Integer显式类型的映射(main()中的var东西),意味着Java可以在调用通用方法consumpStuff()时推断类型。

Inside consumeStuff we can treat the contents of the inner list as a number. 在消耗对象内部,我们可以将内部列表的内容视为数字。 We can dig into it, grab a value from the list and pass it to a method/constructor that accepts type Number. 我们可以对其进行挖掘,从列表中获取一个值,然后将其传递给接受Number类型的方法/构造函数。

Meanwhile I found this page about Wildcard-Capture: http://docs.oracle.com/javase/tutorial/java/generics/capture.html 同时,我发现了有关Wildcard-Capture的页面: http : //docs.oracle.com/javase/tutorial/java/generics/capture.html

As I understand it the problem is that the compile generates different types 据我了解,问题是编译生成不同的类型

  • for the local variable: Pair<String,List<? extends TestType>> 对于局部变量: Pair<String,List<? extends TestType>> Pair<String,List<? extends TestType>>
  • and the return value: Pair<String,List<capture#3-of ? extends TestType>> 和返回值: Pair<String,List<capture#3-of ? extends TestType>> Pair<String,List<capture#3-of ? extends TestType>> . Pair<String,List<capture#3-of ? extends TestType>>

The "fix" for this Problem while keeping the original method signature is to parametrize the calling method with <T extends TestType> . 保留原始方法签名的同时,此问题的“修复”是使用<T extends TestType>来参数化调用方法。 For the example it looks like this: 对于示例,它看起来像这样:

public class Test {

    static class TestType {}

    static class Pair<A,B>{}

    public static <T extends TestType> void main( String [] args ) throws Exception {

        Collection<T> val = null;

        List<T> single = testSingle( val ); // OK

        Pair<String,List<T>> pair = testPair( val ); // OK

    }

    static <T extends TestType> List<T> testSingle( Collection<T> val ){
        return null;
    }

    static <T extends TestType> Pair<String,List<T>> testPair( Collection<T> val ){
        return null;
    }

}

(this is my first time to write main like this :) (这是我第一次这样写main :)

This forces the compiler to use the exacly same type for the variable and the method's return type. 迫使编译器对变量和方法的返回类型使用完全相同的类型。

I still feel that the generics are somehow 'buggy' and unpredictable in this regard. 我仍然觉得仿制药在这方面有些“笨拙”且不可预测。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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