无界通配符类型List <! - ? - >和原始类型List之间有什么区别?

时间:2009-08-29 12:12:56

标签: java generics list

您能帮我理解无界通配符类型列表原始类型列表之间的区别吗?

List<?> b;    // unbounded wildcard type
List a;       // raw type


除此之外,任何人都可以帮助我理解什么是有界类型参数列表

List<E extends Number> c;

4 个答案:

答案 0 :(得分:32)

以下是三者的总结:

  • List:没有类型参数的列表。它是一个列表,其元素可以是任何类型 - 元素可以是不同类型的

  • List<?>:具有无界类型参数的列表。它的元素是特定的,但未知的类型; 元素必须都是相同的类型

  • List<T extends E>:一个名为T的类型参数的列表。 T提供的类型必须是扩展E的类型,或者不是参数的有效类型。

答案 1 :(得分:19)

你应该看看Effective Java,第23项:不要在新代码中使用原始类型。

要使用该书中的示例,请考虑以下示例...如果您有一个不关心其中包含哪些元素类型的集合,该怎么办?例如,您想要查看两个集合之间共有多少个元素。您可能会提出以下建议:

public static int numElementsInCommon(Set s1, Set s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

这个例子虽然有效,但由于使用了原始类型,因此不是一个好主意。原始类型根本不是类型安全的......你最终可能会以一种非类型安全的方式修改集合并破坏你的程序。相反,谨慎一点并使用类型安全替代方案:

public static int numElementsInCommon(Set<?> s1, Set<?> s2) {
  int result = 0;
  for (Object o : s1) {
    if (s2.contains(o)) {
      ++result;
    }
  }
  return result;
}

不同之处在于,您只能将null添加到Set<?>,而且您不能假设您从Set<?>中获取的元素。如果您使用原始Set,则可以添加任何内容。 numElementsInCommon方法是一个很好的示例,您甚至不需要添加任何内容,也不需要假设集合中的内容。这就是为什么它是使用?通配符的好选择。

希望这会有所帮助。阅读有效Java中的整个项目,它将变得清晰。

回答你问题的第二部分......记得我说当你使用?通配符时,你不能假设你从集合中取出的元素有什么?如果您确实需要对从集合中删除的对象的接口做出假设,该怎么办?例如,假设您想要跟踪一组Cool事物。

public interface Cool {
  // Reports why the object is cool
  void cool();
}

然后你可能会有这样的代码:

public static void reportCoolness(Set s) {
  for (Object item : s) {
    Cool coolItem = (Cool) item;
    coolItem.cool();
  }
}

这不是类型安全的......您需要确保传入仅包含Cool个对象的集合。要修复它,您可能会说:

public static void reportCoolness(Set<Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

太好了!完全符合您的要求并且类型安全。但是,如果以后你有这个:

public interface ReallyCool extends Cool {
  // Reports why the object is beyond cool
  void reallyCool();
}

由于所有ReallyCool个对象都是Cool,您应该能够执行以下操作:

Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);

但你不能这样做,因为泛型具有以下属性:假设BA的子类,则Set<B>不是Set<A>的子类。对此的技术讨论是“通用类型是不变的”。 (与协变相反)。

要使最后一个示例起作用,您需要通过(安全地)转换Set<Cool>中的每个元素来创建Set<ReallyCool>。为了避免让你的api的客户经历这个讨厌的,不必要的代码,你可以让reportCoolness方法更加灵活:

public static void reportCoolness(Set<? extends Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}

现在,您的方法会使用包含Set元素或Cool的任何子类的任何Cool。所有这些类型都遵循Cool API ...因此我们可以安全地在任何元素上调用cool()方法

有意义吗?希望这会有所帮助。

答案 2 :(得分:4)

关于第一个问题,ListList<?>之间的差异:

两者之间的一个显着区别是,当您使用通配符作为类型时,Collection的类型是未知的,因此add方法将引发编译时错误。

您仍然可以从List<?>中获取值,但您需要显式转换。

答案 3 :(得分:0)

无界通配符类型列表和原始类型列表之间的区别。

两种情况下,我们都可以将任何类型的列表放入此变量中:

List nothing1 = new ArrayList<String>();
List nothing2 = new ArrayList();
List nothing3 = new ArrayList<>();
List nothing4 = new ArrayList<Integer>();

List<?> wildcard1 = new ArrayList<String>();
List<?> wildcard2 = new ArrayList();
List<?> wildcard3 = new ArrayList<>();
List<?> wildcard4 = new ArrayList<Integer>();

但是我们可以将哪些元素放入此对象中?

我们只能将String放入List<String>

List<String> strings = new ArrayList<>();
strings.add("A new string");

我们可以将任何对象放入列表:

List nothing = new ArrayList<>();
nothing.add("A new string");
nothing.add(1);
nothing.add(new Object());

除了{em> null ,我们什么也不能放入List<?>!因为我们使用泛型。而且Java知道它的类型是List,但不知道它是什么类型。而且不要让我们犯错。

结论:List<?>是通用列表,为我们提供了类型安全性

P.S。切勿在代码中使用原始类型。