Using and Programming Generics in J2SE 5.0

本文介绍了Java 5.0中引入的泛型特性,解释了泛型如何提高类型安全性并消除强制类型转换的需求。通过示例展示了如何使用泛型创建类型安全的集合。
http://java.sun.com/developer/technicalArticles/J2SE/generics/


A Java collection is a flexible data structure that can hold heterogeneous objects where the elements may have any reference type. It is your responsibility, however, to keep track of what types of objects your collections contain. As an example, consider adding an int to a collection; since you cannot have collections of primitive data types you must convert the int to the corresponding reference type (i.e. Integer) before storing it in the collection. Now, when the element is extracted from the collection an Object is returned that must be cast to an Integer in order to ensure type safety. All this makes Java programs unnecessarily hard to read and maintain, and are more likely to fail with runtime errors.

If the compiler could keep track of the element type, you do not need to keep track of what collections you have and the need for casting would be eliminated. This would make programs easier to read and maintain, and less likely to fail at runtime. J2SE 5.0 has added a new core language feature known as generics (also known as parameterized types), that provides compile-time type safety for collections and eliminate the drudgery of casting. The effort of adding generics to Java is led by Sun Microsystems as JSR 14 under the Java Community Process (JCP).

Generics are one of the most frequently requested language extensions to Java, and they have been finally added in J2SE 5.0. This article provides an introduction to programming with generics.

The Need for Generics

The motivation for adding generics to the Java programming language stems from the lack of information about a collection's element type, the need for developers to keep track of what type of elements collections contain, and the need for casts all over the place. Using generics, a collection is no longer treated as a list of Object references, but you would be able to differentiate between a collection of references to Integers and collection of references to Bytes. A collection with a generic type has a type parameter that specifies the element type to be stored in the collection.

As an example, consider the following segment of code that creates a linked list and adds an element to the list:

LinkedList list = new LinkedList();
list.add(new Integer(1));
Integer num = (Integer) list.get(0);

As you can see, when an element is extracted from the list it must be cast. The casting is safe as it will be checked at runtime, but if you cast to a type that is different from, and not a supertype of, the extracted type then a runtime exception, ClassCastException will be thrown.

Using generic types, the previous segment of code can be written as follows:

LinkedList<Integer> list = new LinkedList<Integer>();
list.add(new Integer(1));
Integer num = list.get(0);

Here we say that LinkedList is a generic class that takes a type parameter, Integer in this case.

As you can see, you no longer need to cast to an Integer since the get() method would return a reference to an object of a specific type (Integer in this case). If you were to assign an extracted element to a different type, the error would be at compile-time instead of run-time. This early static checking increases the type safety of the Java language.

To reduce the clutter, the above example can be rewritten as follows...using autoboxing:

LinkedList<Integer> list = new LinkedList<Integer>();
list.add(1);
int num = list.get(0);

As a complete example, consider the following class, Ex1, which creates a collection of two Strings and one Integer, and then prints out the collection:

Ex1.java

import java.util.*;

public class Ex1 {

private void testCollection() {
List list = new ArrayList();
list.add(new String("Hello world!"));
list.add(new String("Good bye!"));
list.add(new Integer(95));
printCollection(list);
}

private void printCollection(Collection c) {
Iterator i = c.iterator();
while(i.hasNext()) {
String item = (String) i.next();
System.out.println("Item: "+item);
}
}

public static void main(String argv[]) {
Ex1 e = new Ex1();
e.testCollection();
}
}

Again, an explicit cast is required in the printCollection method. This class compiles fine, but throws a CLassCastException at runtime as it attempts to cast an Integer to a String:

Item: Hello world!
Item: Good bye!
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer
at Ex1.printCollection(Ex1.java:16)
at Ex1.testCollection(Ex1.java:10)
at Ex1.main(Ex1.java:23)

Using Generics

Using generics, the Ex1 class above can be written as follows:

Ex2.java

import java.util.*;

public class Ex2 {

private void testCollection() {
List<String> list = new ArrayList<String>();
list.add(new String("Hello world!"));
list.add(new String("Good bye!"));
list.add(new Integer(95));
printCollection(list);
}

private void printCollection(Collection c) {
Iterator<String> i = c.iterator();
while(i.hasNext()) {
System.out.println("Item: "+i.next());
}
}

public static void main(String argv[]) {
Ex2 e = new Ex2();
e.testCollection();
}
}

Now, if you try to compile this code, a compile-time error will be produced informing you that you cannot add an Integer to a collection of Strings. Therefore, generics enable more compile-time type checking and therefore mismatch errors are caught at compile-time rather than at run-time.

You may have already noticed the new syntax used to create an instance of ArrayList (List<String> list = new ArrayList<String>()). ArrayList is now a parameterized type. A parameterized type consists of a class or interface name E and a parameter section <T1, T2, ..., Tn>, which must match the number of declared parameters of E, and each actual parameter must be a subtype of the formal parameter's bound types. The following segment of code shows parts of the new class definition for ArrayList:

public class ArrayList<E> extends AbstractList<E> implements List<E>, 
RandomAccess, Cloneable, Serializable {
// ...
}

Here E is a type variable, which is an unqualified identifier. It simply acts as a placeholder for a type to be defined when the list is used.

Implementing Generic Types

In addition to using generic types, you can implement your own. A generic type has one or more type parameters. Here is an example with only one type parameter called E. A parameterized type must be a reference type, and therefore primitive types are not allowed to be parameterized types.

interface List<E> {
void add(E x);
Iterator<E> iterator();
}

interface Iterator<E> {
E next();
boolean hasNext();
}


class LinkedList<E> implements List<E> {
// implementation
}

Here, E represents the type of elements contained in the collection. Think of E as a placeholder that will be replaced by a concrete type. For example, if you write LinkedList<String> then E will be replaced by String.

In some of your code you may need to invoke methods of the element type, such as Object's hashCode() and equals(). Here is an example that takes two type parameters:

class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

// ...

public V get(Object k) {
...
int hash = k.hashCode();
...
}
// ...
}

The important thing to note is that you are required to replace the type variables K and V by concrete types that are subtypes of Object.

Generic Methods

Genericity is not limited to classes and interfaces, you can define generic methods. Static methods, nonstatic methods, and constructors can all be parameterized in almost the same way as for classes and interfaces, but the syntax is a bit different. Generic methods are also invoked in the same way as non-generic methods.

Before we see an example of a generics method, consider the following segment of code that prints out all the elements in a collection:

public void printCollection(Collection c) {
Iterator i = c.iterator();
for(int k = 0;k<c.size();k++) {
System.out.println(i.next());
}
}

Using generics, this can be re-written as follows. Note that the Collection<?> is the collection of an unknown type.

void printCollection(Collection<?> c) {
for(Object o:c) {
System.out.println(o);
}
}

This example uses a feature of generics known as wildcards.

Wildcards

There are three types of wildcards:

  1. "? extends Type": Denotes a family of subtypes of type Type. This is the most useful wildcard
  2. "? super Type": Denotes a family of supertypes of type Type
  3. "?": Denotes the set of all types or any

As an example of using wildcards, consider a draw() method that should be capable of drawing any shape such as circle, rectangle, and triangle. The implementation may look something like this. Here Shape is an abstract class with three subclasses: Circle, Rectangle, and Triangle.

public void draw(List<Shape> shape) {
for(Shape s: shape) {
s.draw(this);
}
}

It is worth noting that the draw() method can only be called on lists of Shape and cannot be called on a list of Circle, Rectangle, and Triangle for example. In order to have the method accept any kind of shape, it should be written as follows:

public void draw(List<? extends Shape> shape) {
// rest of the code is the same
}

Here is another example of a generics method that uses wildcards to sort a list into ascending order. Basically, all elements in the list must implement the Comparable interface.

public static <T extends Comparable<? super T>> void sort(List<T> list) {
Object a[] = list.toArray();
Arrays.sort(a);
ListIterator<T> i = list.listIterator();
for(int j=0; j<a.length; j++) {
i.index();
i.set((T)a[j]);
}
}

Changes to the Java Specification, JVM, and APIs

In order to support generic types, some modifications are necessary to the Java programming language, the Java virtual machine1, and the Java APIs. The notable changes to the Java APIs are related to the Collection hierarchy in the java.util package, changes to the java.lang.Class class, and the java.lang.reflect package so that it is possible to examine a type, method, constructor or field declaration and obtain generic type information. If you like to learn about the exact changes, please see JSR 14: Adding Generics to the Java Programming Language.

Behind the Scenes

Generics are implemented by the Java compiler as a front-end conversion called erasure, which is the process of translating or rewriting code that uses generics into non-generic code (that is, maps the new syntax to the current JVM specification). In other words, this conversion erases all generic type information; all information between angle brackets is erased. For example, LinkedList<Integer> will become LinkedList. Uses of other type variables are replaced by the upper bound of the type variable (for example, Object), and when the resulting code is not type correct, a cast to the appropriate type is inserted.

Java Generics vs. C++ Templates

While generics look like the C++ templates, it is important to note that they are not the same. Generics simply provide compile-time type safety and eliminate the need for casts. The main difference is encapsulation: errors are flagged where they occur and not later at some use site, and source code is not exposed to clients. Generics use a technique known as type erasure as described above, and the compiler keeps track of the generics internally, and all instances use the same class file at compile/run time.

A C++ template on the other hand is just a fancy macro processor; whenever a template class is instantiated with a new class, the entire code for the class is reproduced and recompiled for the new class.

Conclusion

Generics are a new core feature in J2SE 5.0, and a major addition to the core language. This feature provides a useful abstract and compile-time type safety for collections and eliminates the drudgery of casting. This article provided an overview and introduction to Java generics, and showed how to use generics as well as write your own. The examples provided in this article demonstrate how useful this new core feature is.

 
当前,全球经济格局深刻调整,数字化浪潮席卷各行各业,智能物流作为现代物流发展的必然趋势和关键支撑,正迎来前所未有的发展机遇。以人工智能、物联网、大数据、云计算、区块链等前沿信息技术的快速迭代与深度融合为驱动,智能物流不再是传统物流的简单技术叠加,而是正在经历一场从自动化向智能化、从被动响应向主动预测、从信息孤岛向全面互联的深刻变革。展望2025年,智能物流系统将不再局限于提升效率、降低成本的基本目标,而是要构建一个感知更全面、决策更精准、执行更高效、协同更顺畅的智慧运行体系。这要求我们必须超越传统思维定式,以系统化、前瞻性的视角,全面规划和实施智能物流系统的建设。本实施方案正是基于对行业发展趋势的深刻洞察和对未来需求的精准把握而制定。我们的核心目标在于:通过构建一个集成了先进感知技术、大数据分析引擎、智能决策算法和高效协同平台的综合智能物流系统,实现物流全链路的可视化、透明化和智能化管理。这不仅是技术层面的革新,更是管理模式和服务能力的全面提升。本方案旨在明确系统建设的战略方向、关键任务、技术路径和实施步骤,确保通过系统化部署,有效应对日益复杂的供应链环境,提升整体物流韧性,优化资源配置效率,降低运营成本,并最终为客户创造更卓越的价值体验。我们致力于通过本方案的实施,引领智能物流迈向更高水平,为构建现代化经济体系、推动高质量发展提供强有力的物流保障。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值