-
可以为一个接口或类声明一个或多个类型参数,它们是写在尖括号中,当你声明一个变量属于时应该被提供到接口或类,或者当你创建一个新的类的实例。
-
我们在前面的部分看到了一个例子。 这是另一个:
List<String> words = new ArrayList<String>(); words.add("Hello "); words.add("world!"); String s = words.get(0)+words.get(1); assert s.equals("Hello world!");
-
在集合框架中,类
ArrayList<E>
实现接口List<E>
。 这个简单的代码片段声明一个包含字符串列表的变量,创建一个ArrayList
的实例,将两个字符串添加到 列表中,并再次获取它们。 -
在泛型之前的
Java
中,相同功能的代码将被编写如下:List words = new ArrayList(); words.add("Hello "); words.add("world!"); String s = ((String)words.get(0))+((String)words.get(1)) assert s.equals("Hello world!");
-
如果没有泛型,则省略类型参数,但只要从列表中提取元素,就必须显式地转换。
-
事实上,从上面两个来源编译的字节码将是相同的。 我们说泛型是通过擦除来实现的,因为类型
List<Integer>
,List<String>
和List<List<String>
都在 运行时由相同的类型List
。 我们还使用“类型擦除”来描述将第一个程序转换为第二个程序的过程。术语擦除是一个轻微的误称,因为该过程擦除了类型参数但增加了 代码。 -
泛型隐式执行没有泛型明确执行的相同的强制转换。如果使用这样转换可能会失败,可能很难调试使用泛型编写的代码。 这就是为什么可以肯定泛型具有以下保证:
保证:通过泛型编译添加的隐式转换从不失败。
-
这个保证也有一些细小的瑕疵:只有在没有检查的情况下才适用编译器发出警告。 后来我们会详细讨论一下导致发出未经检查的警告以及如何最小化其影响。
-
通过实现泛型擦除有一些重要的影响。 它保持编写简单,在泛型不添加任何根本的新东西。 它使代码变得简单,因为
List
只有一个实现,而不是每个类型的一个 版本。 同时它简化了版本升级,因为可以在非泛型和泛型中访问同一个库形式。 -
最后值得阐述一点。 这意味着你不会由于维护两个版本的库而烦恼:一个非通用的旧版本的作品与
Java1.4
或更早版本,以及与Java5
和6
一起工作的通用版本 ,与不使用泛型的代码看起来一样。 无需一次切换到泛型,只需更新即可演变您的代码一个程序包,一个类或一个方法开始使用泛型。 我们甚至解释您如何可以为遗留 的代码声明泛型类型。 (当然,仅保证当您添加与传统代码相匹配的泛型类型时,上述提及才有效。) -
通过类型擦除来实现泛型的另一个结果是数组类型不同参数化类型的关键。执行
new String[size]
分配一个数组,并在该数组中存储一个指定其组件是类型是字符串。 相反,执行:
new ArrayList<String>()
```
分配一个列表,但不在列表中存储任何元素类型的指定。在行话中,我们说`Java`会将数组元素类型作为数据类型,但并不是指定数组元素的类型元素类型(或其他通
用类型)。 稍后,我们将看到这个设计如何缓解进化(见第5章),仅仅强制转换,实例测试和数组创建(见第6章)。
- 泛型与模板`Java`中的泛型类似于`C++`中的模板。有关于`Java`泛型之间的关系需要牢记两件重要的事情和`C++`模板:语法和语义。
语法和故意相似语义是故意不同的。
- 在句法上,尖括号是被选中的,因为它们对于C ++用户来说是熟悉的因为方括号很难解析。 但是,有一个区别句法。
在`C++`中,嵌套参数需要额外的空间,所以你看到这样的事情:
```java
List< List<String> >
在Java中,不需要任何空格,可以这样写:
List<List<String>>
如果您愿意,您可以使用额外的空间,但不是必需的。(在C++
中,一个问题出现是因为>>
没有空格表示右移运算符。 Java修复了问题在语法中的一个窍门。)
- 从语义上讲,Java泛型是通过擦除来定义的,而C ++模板是被定义的通过扩张。 在C ++模板中,编译一个新类型的模板的每个实例分别。 如果使用整数列表,字符串列表和字符串列表列表,那么将会有三个版本的代码。 如果你使用一百种不同类型的清单,那么会有成为代码的一百个版本 - 一个被称为代码膨胀的问题。 在Java中,不管你使用多少种类型的列表,总是有一个版本的代码,所以膨胀不会发生。
- 扩展可能导致比删除更有效的实施,因为它提供更多优化的机会,特别是对于像int这样的原始类型。 代码那就是操纵大量数据 - 例如科学的大数组计算 - 这种差异可能是重大的。 但是,在实践中,为了大多数目的效率的差异并不重要, 而代码膨胀造成的问题可能至关重要。
- 在
C++
中,你也可以用一个常量值而不是一个类型实例化一个模板,使模板成为一种“程序建设”在编译时执行任意复杂的计算成为可能。Java
泛型被有意地限制为类型,以使它们简单易懂。