我有以下代码:
public static<F, S> void xyz() {
class Pair<X, Y> {}
class Role<F> {}
Map<?, List<Pair<?, ?>>> map = null;
Role<F> role = null;
List<Pair<F, S>> result = (List<Pair<F, S>>) map.get(role);
}
不幸的是,Java抱怨最后一行的演员阵容。为什么会这样?
在我的选择中,这是违反直觉的。在每个可能存在正确类型对应关系的地方都应该允许演员。
答案 0 :(得分:1)
演员没有任何意义。这不是扩大演员的逆转。
一个类似的可能演员,我们有一个通配符参数:
List<String> ls;
List<?> cast = ls;
在这种情况下,我无法随后向Integer
/ ls
对象添加cast
。
cast.add(Integer.valueOf(0)); // Not going to work.
如果泛型参数本身是带通配符的泛型,那么情况就大不相同了。
List<List<String>> strses;
List<List<?>> cast = strses; // Suppose this actually worked.
List<Integer> ints;
List<?> wildInts = ints;
cast.add(0, wildInts); // Good.
List<String> wasInts = strses.get(0); // Oops!!
答案 1 :(得分:1)
创建对象时,它具有确切的类型。此类型可能是ArrayList<Pair<F, S>>
,当然这是List<Pair<F, S>>
的子类型。然而,正如汤姆指出的那样,这不是List<Pair<?,?>>
的子类型。因此,你的演员阵容正确的唯一方法是在其他地方有一个不合理的演员。如果仿制药在某些时候具体化,那么你的演员阵容或那些不合理的演员阵容都会破裂。这就是为什么演员被拒绝的理由。
答案 2 :(得分:1)
泛型不是一个全新的类型系统。在内部,它都基于强制转换(因为字节码(向后)兼容性)。
扩展汤姆的答案,让我们做最简单的例子:
List<? extends Object> genericList; // List<?> is short for List<? extends Object>
List<String> concreteList = new LinkedList<String>();
genericList = concreteList;
concreteList = (List<String>) genericList; //(caution: uncheck cast)
因此,这是有效的,因为String扩展了Object。让我们继续前进并列出清单:
List<List<?>> listOfGenericLists;
List<List<String>> listOfStrings = new LinkedList<List<String>>();
//does not work "only because" String extends Object:
listOfGenericLists = listOfStrings; //compile error
这与您的示例中的错误相同。直觉是说这应该有效,因为String扩展了Object,因此List<String>
应该扩展List<Object>
。嗯,事实并非如此。你可能不会对此感到惊讶:
列表&gt; listOfIntegers;
listOfIntegers = listOfStrings; //也编译错误
让我们看看当我们明确直觉时会发生什么。我们实际上是从列表中扩展出来的:
List<? extends List<?>> genericListOfLists = new LinkedList<List<?>>();
List<? extends List<String>> genericListOfStringLists = new LinkedList<List<String>>();
genericListOfLists = listOfStrings;
genericListOfStringLists = listOfStrings;
listOfStrings = (List<List<String>>) genericListOfLists; //(caution: unchecked cast)
listOfStrings = (List<List<String>>) genericListOfStringLists; // works as before (caution: uncheck cast)
阿公顷!现在直觉回到了赛道上。对? 实际上没有:
List<? extends List<Integer>> listOfIntegerLists;
listOfIntegerLists = (List<List<Integer>>) genericListOfLists; //(caution: uncheck cast)
listOfIntegerLists = (List<List<Integer>>) genericListOfStringLists; //OUCH (no compilation error just an unchecked cast!)
原因是 - 如前所述:从类型的角度来看,所有泛型都是相同的,并且在类型级别上进行投射。
我可以向Maurice Naftalin和Philip Wadler(O'Reilly)推荐Java Generics and Collections。
答案 3 :(得分:-1)
您希望将列表的通用实例转换为另一个列表的通用实例:
List<Pair<?, ?>> ==>> List<Pair<F, S>>
你无法通过以下方式解决这个问题:
List<Pair<?, ?>> result = (List<Pair<?, ?>>) map.get(role);
答案 4 :(得分:-1)
“在每个可能存在正确类型对应关系的地方都应该允许演员表演。”在这种情况下,没有正确类型对应的可能性。
如果A和B具体且不相同,则对象永远不能是List<A>
和List<B>
的实例,即使A是B的子类型或其他什么。例如,您不能拥有List<String>
和List<Object>
的对象。 (概念上)每个对象都有一个特定的类型参数。你必须先了解这一部分。
您的示例是相同的 - List<Pair<F, S>>
和List<Pair<?, ?>>
。出于与上面的String和Object相同的原因,List<Pair<?, ?>>
类型的对象永远也不能是List<Pair<F, S>>
。
更新:如有帮助,请提供更多说明。 @Konstantin:如果你写Pair<?, ?>
,那么顶级的那些类型参数?
(通配符)是灵活的。 Pair<T1, T2>
与Pair<?, ?>
兼容。但是,在List<Pair<?, ?>>
中,?
不在顶级。顶级的类型参数是Pair<?, ?>
,它不是通配符,因此不灵活。你碰巧让?
更深入了并不重要。如果您有List<? extends Pair<?, ?>>
,则顶级是通配符,并且非常灵活。
您可能想要的是List<? extends Pair<?, ?>>
。也许它会帮助您考虑List<Pair<?, ?>>
和List<? extends Pair<?, ?>>
之间的差异。 List<Pair<?, ?>>
表示这是一个List,它的类型参数正好类型Pair<?, ?>
(没有别的,不是Pair<T1, T2>
等)。 List<? extends Pair<?, ?>>
表示这是一个List,它的类型参数是Pair<?, ?>
的子类型(包括Pair<T1, T2>
)。