When you take an element out of a Collection
, you must cast it to the type of element that is stored in the collection. Besides being inconvenient, this is unsafe. The compiler does not check that your cast is the same as the collection's type, so the cast can fail at run time.
Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.
Here is a simple example taken from the existing Collections tutorial:
// Removes 4-letter words from c. Elements must be strings
static void expurgate(Collection c) {
for (Iterator i = c.iterator(); i.hasNext(); )
if (((String) i.next()).length() == 4)
i.remove();
}
Here is the same example modified to use generics:
// Removes the 4-letter words from c
static void expurgate(Collection<String> c) {
for (Iterator<String> i = c.iterator(); i.hasNext(); )
if (i.next().length() == 4)
i.remove();
}
When you see the code <Type>
, read it as “of Type
”; the declaration above reads as “Collection
of String c
.” The code using generics is clearer and safer. We have eliminated an unsafe cast and a number of extra parentheses. More importantly, we have moved part of the specification of the method from a comment to its signature, so the compiler can verify at compile time that the type constraints are not violated at run time. Because the program compiles without warnings, we can state with certainty that it will not throw a ClassCastException
at run time. The net effect of using generics, especially in large programs, is improved readability and robustness.
To paraphrase Generics Specification Lead Gilad Bracha, when we declare c
to be of type Collection<String>
, this tells us something about the variable c
that holds true wherever and whenever it is used, and the compiler guarantees it (assuming the program compiles without warnings). A cast, on the other hand, tells us something the programmer thinks is true at a single point in the code, and the VM checks whether the programmer is right only at run time.
While the primary use of generics is collections, there are many other uses. “Holder classes,” such as WeakReference
and ThreadLocal
, have all been generified, that is, they have been retrofitted to make use of generics. More surprisingly, class Class
has been generified. Class literals now function as type tokens, providing both run-time and compile-time type information. This enables a style of static factories exemplified by the getAnnotation
method in the new AnnotatedElement
interface:
<T extends Annotation> T getAnnotation(Class<T> annotationType);
This is a generic method. It infers the value of its type parameter T
from its argument, and returns an appropriate instance of T
, as illustrated by the following snippet:
Author a = Othello.class.getAnnotation(Author.class);
Prior to generics, you would have had to cast the result to Author
. Also you would have had no way to make the compiler check that the actual parameter represented a subclass of Annotation
.
Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler. The main advantage of this approach is that it provides total interoperability between generic code and legacy code that uses non-parameterized types (which are technically known as raw types). The main disadvantages are that parameter type information is not available at run time, and that automatically generated casts may fail when interoperating with ill-behaved legacy code. There is, however, a way to achieve guaranteed run-time type safety for generic collections even when interoperating with ill-behaved legacy code.
The java.util.Collections
class has been outfitted with wrapper classes that provide guaranteed run-time type safety. They are similar in structure to the synchronized and unmodifiable wrappers. These “checked collection wrappers” are very useful for debugging. Suppose you have a set of strings, s
, into which some legacy code is mysteriously inserting an integer. Without the wrapper, you will not find out about the problem until you read the problem element from the set, and an automatically generated cast to String
fails. At this point, it is too late to determine the source of the problem. If, however, you replace the declaration:
Set<String> s = new HashSet<String>();
with this declaration:
Set<String> s = Collections.checkedSet(new HashSet<String>(), String.class);
the collection will throw a ClassCastException
at the point where the legacy code attempts to insert the integer. The resulting stack trace will allow you to diagnose and repair the problem.
You should use generics everywhere you can. The extra effort in generifying code is well worth the gains in clarity and type safety. It is straightforward to use a generic library, but it requires some expertise to write a generic library, or to generify an existing library. There is one caveat: You may not use generics (or any other Tiger features) if you intend to deploy the compiled code on a pre-5.0 virtual machine.
If you are familiar with C++'s template mechanism, you might think that generics are similar, but the similarity is superficial. Generics do not generate a new class for each specialization, nor do they permit “template metaprogramming.”
分享到:
相关推荐
初步学习Java的泛型和反射机制,通过一些简单的例子来学习泛型,反射
Java的泛型特性是其语言的一个重要组成部分,自Java SE 1.5版本引入以来,极大地增强了代码的类型安全性,同时也提高了代码的可读性和复用性。泛型允许我们在编写代码时,定义一个可以处理多种数据类型的通用结构,...
泛型与集合 使用 进行初步翻译. 将利用碎片时间进行整理和校对,完整的时间段适合做其他需要大量思考的事,如果你有兴趣欢迎提交PR。 TODO 数据校对 目录 2.4 获取和放置原则 2.5 数组 2.6 通配符与类型参数 2.7 ...
Java教程初步讲解旨在为初学者提供一套全面而深入的学习路径,帮助他们快速掌握这门广泛应用于全球的编程语言。Java以其“一次编写,到处运行”的跨平台特性,以及在企业级应用、移动开发(尤其是Android)、云计算...
- 集合框架支持泛型,提供高效的数据存储和操作。 7. **多线程**: - Java内置对多线程的支持,可以通过实现Runnable接口或继承Thread类创建线程。 - 线程同步机制包括synchronized关键字、wait(), notify(), ...
1. 项目迁移:当一个组织决定从Java转向Delphi,或者希望将部分Java组件整合到Delphi项目中时,Java2Pas可以帮助快速完成代码的初步转换。 2. 代码复用:开发者可以利用Java2Pas将已有的成熟Java库转换为Delphi代码...
13. **泛型**:Java泛型允许在类型中使用类型参数,提高了代码的类型安全性和重用性。 14. **模块系统**:自Java 9开始引入了模块系统,它提高了大型应用程序的可维护性和性能。 15. **Java标准库**:Java附带了...
7. **集合框架**:熟悉ArrayList、LinkedList、HashSet、HashMap等集合类的使用,以及泛型的概念。 8. **输入/输出流**:理解I/O流的概念,包括文件操作、字节流和字符流的使用。 9. **多线程**:学习如何创建和...
书中首先对Java语言的基础知识进行了详细介绍,这包括Java的核心概念、基础语法和基本结构,旨在让读者能够对Java语言有一个初步的认识和理解。之后,书中通过具体的实例,对Java语言进行了系统化的讲解,帮助读者更...
《Java程序设计》共分12章,内容包括Java语言概述、面向对象编程初步、Java的基本语法、类库与数组、面向对象编程深入、图形用户界面编程、异常处理和输入输出,以及多线程编程等内容。 本书讲解Java程序设计知识...
九、Java泛型 泛型是Java 5引入的新特性,增强了类型安全性和代码可读性。学员需要理解泛型的基本用法和限制。 十、Java 8及以后的新特性 包括Lambda表达式、Stream API、日期和时间API等新特性,都是提升Java编程...
2. **环境配置**:解释如何下载和安装Java Development Kit (JDK),以及如何设置Java开发环境,如配置JAVA_HOME变量和集成开发环境(IDE)的初步使用。 3. **语法基础**:涵盖Java的基本语法,包括数据类型(如整型...
Java泛型引入于1.5版本,允许在定义集合、接口和方法时指定类型参数,增强了类型安全,减少了强制类型转换的麻烦。泛型通配符如、extends T、super T提供了更灵活的类型约束。 十、Lambda表达式 自Java 8起,引入...
1. **Java简介**:介绍Java的历史、特点、应用领域以及与其它编程语言的对比,使学习者对Java有一个初步认识。 2. **环境配置**:讲解如何安装Java Development Kit (JDK) 和设置环境变量,为后续开发工作奠定基础...
15. **Java编程工具**:如Eclipse、IntelliJ IDEA等集成开发环境的使用,以及Maven或Gradle构建工具的初步接触。 通过学习"Java编程逻辑",读者不仅可以掌握Java语言的基础,还能建立起良好的编程思维,为进一步...
书中不仅覆盖了编译器的各个阶段,还详细介绍了如何利用Java语言的特点来优化编译过程,比如利用泛型、反射等高级特性。 ### 结论 对于想要深入了解Java内部机制、提升编程技能或从事软件开发工具链研究的专业人士...
本教程"Java经典教程,初学者的最好的宝典,从入门到精通(下)"是专门为那些希望掌握Java编程技能的人设计的,尤其是对Java有初步兴趣并希望深入学习的读者。下面将详细阐述Java的一些核心知识点,这些内容可能包含...
9. **泛型**:Java泛型是Java SE 5.0引入的新特性,用于增强类型安全性和代码可读性,减少强制类型转换。 10. **接口与模块化**:Java 8引入了默认方法,Java 9引入了模块系统(Project Jigsaw),这些都是现代Java...