[英]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
,将TestType
为Object
:
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 据我了解,问题是编译生成不同的类型
Pair<String,List<? extends TestType>>
对于局部变量: Pair<String,List<? extends TestType>>
Pair<String,List<? 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>>
. 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.