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

Ash*_*wal 23 java generics list

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

List<?> b;    // unbounded wildcard type
List a;       // raw type
Run Code Online (Sandbox Code Playgroud)


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

List<E extends Number> c;
Run Code Online (Sandbox Code Playgroud)

Joh*_*lla 32

以下是三者的总结:

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

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

  • List<T extends E>:带有类型参数的列表T.提供的类型T必须是扩展的类型E,或者它不是参数的有效类型.

  • "元素必须都是相同的类型"这种区别不存在.type参数可以是Object,在这种情况下,元素可以是任何类型 (4认同)
  • 据我所知,Java在编译程序时会丢失有关泛型类型的所有信息,因此它们最终都是"List".那里没有类型推断,它们都是对象. (2认同)

Tom*_*Tom 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

希望这可以帮助.阅读有效Java中的整个项目,它将变得清晰.

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

public interface Cool {
  // Reports why the object is cool
  void cool();
}
Run Code Online (Sandbox Code Playgroud)

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

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

这不是类型安全的......你需要确保传入一个只有Cool对象的集合.要解决它,你可能会说:

public static void reportCoolness(Set<Cool> s) {
  for (Cool coolItem : s) {
    coolItem.cool();
  }
}
Run Code Online (Sandbox Code Playgroud)

这很棒!完全符合您的要求并且类型安全.但是,如果以后你有这个:

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

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

Set<ReallyCool> s = new HashSet<ReallyCool>();
// populate s
reportCoolness(s);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

合理?希望这可以帮助.


akf*_*akf 5

关于第一个问题,之间的区别ListList<?>

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

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