什么是泛型?
JDK 5.0(2004 年发布)向Java语言中引入了泛型类型(泛型)和关联的语法。基本上讲,一些当时熟悉的 JDK 类被替换为了等效的泛型。泛型是一种编译器机制,您可通过该机制获取通用的代码并参数化(或模板化)剩余部分,从而以一种一般化方式创建(和使用)一些类型的实体(比如类或接口和方法)。这种编程方法被称为泛型编程。
泛型实战
要了解泛型有何作用,可以考虑一个在 JDK 中存在已久的类的示例:java.util.ArrayList
,它是一个由数组提供支持的 Object
的 List
。
清单 1 展示了如何实例化 java.util.ArrayList
。
ArrayList arrayList = new ArrayList();
arrayList.add("A String");
arrayList.add(new Integer(10));
arrayList.add("Another String");
// So far, so good.
清单 2. 尝试访问 ArrayList
中的元素可以看到,ArrayList
具有不同的形式:它包含两个 String
类型和一个 Integer
类型。在 JDK 5.0 之前,Java语言对此行为没有任何约束,这导致了许多编码错误。例如,在 清单 1 中,目前为止看起来一切正常。但要访问 ArrayList
中的元素应该怎么办,清单 2 会尝试采用哪种方法?
ArrayList arrayList = new ArrayList();
arrayList.add("A String");
arrayList.add(new Integer(10));
arrayList.add("Another String");
// So far, so good.
processArrayList(arrayList);
// In some later part of the code...
private void processArrayList(ArrayList theList) {
for (int aa = 0; aa < theList.size(); aa++) {
// At some point, this will fail...
String s = (String)theList.get(aa);
}
}
如果以前不知道 ArrayList
中包含的内容,则必须检查想要访问的元素,查看是否能处理这种类型的元素,否则您可能会遇到 ClassCastException
。
借助泛型,您可以指定放入 ArrayList
中的项目类型。清单 3 展示了如何做,以及在您尝试添加一个错误类型的对象(第 3 行)时会发生什么。
清单 3. 使用泛型的第二次尝试
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("A String");
arrayList.add(new Integer(10));// compiler error!
arrayList.add("Another String");
// So far, so good.
processArrayList(arrayList);
// In some later part of the code...
private void processArrayList(ArrayList<String> theList) {
for (int aa = 0; aa < theList.size(); aa++) {
// No cast necessary...
String s = theList.get(aa);
}
}
迭代泛型
泛型使用处理一些实体(比如 List
)的特殊语法增强了 Java 语言,您通常可能希望逐个元素地处理这些实体。举例而言,如果想迭代 ArrayList
,可以将 清单 3 中的代码重写为:
private void processArrayList(ArrayList<String> theList) {
for (String s : theList) {
String s = theList.get(aa);
}
}
参数化的类
此语法适用于任何 Iterable
(即实现了 Iterable
接口)的对象类型。
参数化的类对集合非常有用,所以集合是以下示例的上下文。考虑 List
接口,它表示一个有序的对象集合。最常见的使用场景为,向 List
添加项,然后按索引或迭代 List
来访问这些项。
如果考虑参数化一个类,请考虑是否满足以下条件:
- 一个核心类位于某种包装器的中心:也就是说,类中心的 “东西” 可广泛地应用,它周围的特性(例如属性)是相同的。
- 行为是相同的:无论类中心的 “东西” 是什么,都会执行完全相同的操作。
根据这两个条件,可以看到集合满足以下要求:
- 这个 “东西” 就是组成集合的类。
- 操作(比如
add
、remove
、size
和clear
)完全相同,无论集合由哪些对象组成。
一个参数化的 List
在泛型语法中,创建 List
的代码类似于:
List<E> listReference = new concreteListClass<E>();
所以,要创建一个由
(表示元素)是我之前提到的 “东西”。java.lang.Integer
组成的ArrayList
,可以这样做:EconcreteListClass
是 JDK 中您实例化的类。该 JDK 包含多个 List<E>
实现,但您使用了 ArrayList<E>
。您可能看到的泛型类的另一种形式是 Class<T>
,其中 T
表示类型。在 Java 代码中看到 E
时,它通常指的是某种类型的集合。当您看到 T
时,它表示一个参数化的类。
List<Integer> listOfIntegers = new ArrayList<Integer>();
现在假设您想要创建自己的参数化类,它名为 SimpleList
且包含 3 个方法:
add()
将一个元素添加到SimpleList
的末尾处。size()
返回SimpleList
中当前的元素数量。clear()
完全清除SimpleList
的内容。
清单 4 给出了参数化 SimpleList
的语法。
package com.makotojava.intro;
import java.util.ArrayList;
import java.util.List;
public class SimpleList<E> {
private List<E> backingStore;
public SimpleList() {
backingStore = new ArrayList<E>();
}
public E add(E e) {
if (backingStore.add(e))
return e;
else
return null;
}
public int size() {
return backingStore.size();
}
public void clear() {
backingStore.clear();
}
}
SimpleList
可使用任何 Object
子类来参数化。要创建并使用一个由java.math.BigDecimal
对象组成的 SimpleList
,可以这样做:
package com.makotojava.intro;
import java.math.BigDecimal;
import java.util.logging.Logger;
import org.junit.Test;
public class SimpleListTest {
@Test
public void testAdd() {
Logger log = Logger.getLogger(SimpleListTest.class.getName());
SimpleList<BigDecimal> sl = new SimpleList<>();
sl.add(BigDecimal.ONE);
log.info("SimpleList size is : " + sl.size());
sl.add(BigDecimal.ZERO);
log.info("SimpleList size is : " + sl.size());
sl.clear();
log.info("SimpleList size is : " + sl.size());
}
}
而且您会获得以下输出:
Sep 20, 2015 10:24:33 AM com.makotojava.intro.SimpleListTest testAdd
INFO: SimpleList size is: 1 Sep 20, 2015 10:24:33 AM com.makotojava.intro.SimpleListTest testAdd
INFO: SimpleList size is: 2 Sep 20,
2015 10:24:33 AM com.makotojava.intro.SimpleListTest testAdd
INFO: SimpleList size is: 0
参数化的方法
有时,您可能不想参数化整个类,而是只想参数化一两个方法。在这种情况下,可以创建一个泛型方法。考虑清单 5 中的实例,其中使用方法 formatArray
来创建数组内容的字符串表示。
清单 5. 一个泛型方法
public class MyClass {
// Other possible stuff... ignore...
public <E> String formatArray(E[] arrayToFormat) {
StringBuilder sb = new StringBuilder();
int index = 0;
for (E element : arrayToFormat) {
sb.append("Element ");
sb.append(index++);
sb.append(" => ");
sb.append(element);
sb.append('\n');
}
return sb.toString();
}
// More possible stuff... ignore...
}
无需参数化 MyClass
,可以将您想要使用的一个方法泛型化,创建一个适合任何元素类型的一致的字符串表示。
实际上,您会发现使用参数化的类和接口的频率比方法更高,但现在只需知道可以在需要的时候使用该功能。
转自:https://www.ibm.com/developerworks/cn/java/j-perry-generics/index.html