A 和A ?

我是一名新的java学习者。 最近我正在阅读Generic编程,让我对此感到困惑……

A and A 

首先,这些是在不同环境中使用的完全不同的结构。

A是generics类型声明的一部分,例如

 public class A { ... } 

它声明了类型参数为Tgenerics类型A ,并在T上引入了一个绑定,因此T必须是B的子类型。


A A是带有通配符的参数化类型,它可以在变量和方法声明等中使用,作为普通类型:

 A a = ...; public void foo(A a) { ... } 

变量声明如A a A a表示A a类型是A参数化为B某个子类型。

例如,鉴于此声明

 List l; 

您可以:

  • Number的某个子类型的List分配给l

     l = new ArrayList(); 
  • 从该列表中获取Number类型的对象:

     Number n = l.get(0); 

但是,您不能将任何内容放入列表l因为您不知道列表的实际类型参数:

 Double d = ...; l.add(d); // Won't compile 

? 就是所谓的通配符 。 这意味着任何延伸B.

当您编写List ,您的List只能包含T类型的元素。

当你写List List ,您的List可以包含任何extends String元素

我希望我很清楚,因为这些概念很复杂。

(长评,不是答案)

完全不同的东西。 语法的相似性是Java所犯的严重错误。 而这个错误会导致更大的错误 – 许多人试图将通配符理解为类型参数(即通配符捕获)

没有A A直到Java发明它。 在此之前,研究人员使用的语法是A 。 Java认为这对我们糟糕的程序员来说太神秘了,所以它发明了语法A A在第一轮似乎更好看。

嗯,这是多么冗长和丑陋。 如果API有一些通配符,它​​看起来像符号的呕吐物。

但最糟糕的是它引起的混乱。 它看起来像一个类型参数。 Java是故意这样做的! Java并不相信它的程序员可以理解协变类型,所以语法上它使得可变类型看起来像参数化类型,引导程序员进入错误的理解方式,不可否认,这在某些场合很有用,但最终会让人无能为力。

generics通常是一个复杂的主题,特别是在Java中。 但基本上区别在于:

T延伸B

有一个特定的类型T,它仅限于B或B的子类,但它是一个特定的知识类型。 任何普通的旧generics声明都像是这样说: 通过说T扩展B,我们说T比任何对象更有限,它必须特别是B的类型。

? 延伸B.

这意味着generics类型是未知的,确切地说,但我们可以说它是扩展B.它可能是B,它可能是B的子类。使用通配符和单词extends,它意味着你可以从对象中获取B,但是你不能以类型安全的方式在对象中放置任何东西。 (?super B意思相反 – 你可以把一些东西放在B的B或B的超类的方法参数中,但你不能确定方法的返回值是什么。)