为什么以下演员导致编译错误?

时间:2012-02-14 06:13:50

标签: java generics types casting

我有以下代码:

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抱怨最后一行的演员阵容。为什么会这样?

在我的选择中,这是违反直觉的。在每个可能存在正确类型对应关系的地方都应该允许演员。

5 个答案:

答案 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>)。