Java泛型总结
概述
泛型JDK1.5之引入,其含义是 “参数化类型”。即在定义时将具体的类型参数化,在使用时再将具体的类型传入,即数据的具体的类型是一个参数,在真正使用时传入实际的类型。例如:
void testNonGenericArrayList() {
List list = new ArrayList();
list.add("ABC");
list.add(new Integer(100));
for (Object o : list) {
String s = (String) o;
System.out.print("Object is " + s);
}
}
上述的代码回出现 ClassCastException。因为Object无法转换成String类型。如果将List改为使用泛型定义
List<String> list = new ArrayList<String>();
那么在编译阶段
list.add(new Integer(100));
就会出现异常。
泛型使用方法
泛型可以作用在类,接口,方法中,使用了泛型的类,接口,方法就被称为泛型类,泛型接口,泛型方法。
泛型类
通过泛型类,可以抽象对一系列类型的通用操作,一个泛型类的基本写法为:
static class GenericClass<T> {
T mField;
GenericClass(T field) {
mField = field;
}
String getDescription() {
return "Class is " + mField.getClass() + " values is " + mField;
}
}
其中<T>即为参数化类型,使用时中需要以真正的类型代替。使用如下:
GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
输出结果:
stringGeneric :Class is class java.lang.String values is ABC
integerGeneric :Class is class java.lang.Integer values is 65535
从代码看,这个泛型类抽象了“打印承运类名称和值的操作”。
泛型接口
泛型接口的定义方法和泛型类类似
interface ComputableItem<T> {
T doubleSelf();
}
接口继承
继承接口有两种方法:
- 不传人参数化类型
interface AdvancedComputableItem<T> extends ComputableItem<T> {
int tripeSelf();
}
这种情况下子接口必须包含和父接口一致的参数化类型。
- 传入参数化类型
interface ComputableString extends ComputableItem<String> {
String tripeSelf();
}
这种情况下子接口无需包含参数化类型。
接口实现
接口的实现类有下面三种形式:
- 传入实例化类型
class AdvancedComputableInteger implements AdvancedComputableItem<Integer> {
Integer mField;
public Integer doubleSelf() {
return mField * 2;
}
public Integer tripeSelf() {
return mField * 3;
}
}
- 继承已经传入了实例化类型的子类
class ConnectableString implements ComputableString {
String mField;
public String doubleSelf() {
return mField + " : " + mField;
}
public int tripeSelf() {
return mField.hashCode() % 5 * 3;
}
}
- 通过泛型类实现
class NullComputableItem<T> implements ComputableItem<T> {
public T doubleSelf() {
return null;
}
}
泛型方法
泛型方法是泛型中使用较为复杂的,一个例子如下:
public <T> T createT(Class<T> tClass) {
T t = null;
try {
t = tClass.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return t;
}
其中<T>表示结构化参数, 使用方法如下:
String str = createT(String.class);
泛型通配符
观察下述代码:
/**
* @param args
*/
public static void main(String[] args) {
GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
printGeneric(numberGeneric);
printGeneric(integerGeneric);
}
static void printGeneric(GenericClass<Number> number) {
System.out.print("printGeneric :" + number.getDescription() + " \r\n");
}
static class GenericClass<T> {
T mField;
GenericClass(T field) {
mField = field;
}
String getDescription() {
return "Class is " + mField.getClass() + " values is " + mField;
}
}
虽然Integer是Number的子类,但是编译器仍然会提示printGeneric(integerGeneric)错误。因为Integer和Number都只是类型参数而已。JDK提供了一种叫做类型统配符的方法来处理这种情况:
static void printGeneric(GenericClass<?> number) {
System.out.print("printGeneric :" + number.getDescription() + " \r\n");
}
这里的?表示通配符,即类型实参。
泛型边界
假设有三个类,其中People是父类,Male和Female是子类,定义泛型类时,可以限定类型参数的范围
static class People {
protected String dress() {
return "Dress cloth";
}
@Override
public String toString() {
return dress();
}
}
static class Male extends People {
@Override
protected String dress() {
return "Dress Suit";
}
}
static class Female extends People {
@Override
protected String dress() {
return "Dress Skirt";
}
}
static class GenericClass<T extends People> {
T mField;
GenericClass(T field) {
mField = field;
}
String getDescription() {
return "Class is " + mField.getClass() + " values is " + mField;
}
}
将类型参数限定后,编译器会对不符合限定的类型实参报错,例如下面这种写法:
GenericClass<Integer> integerGeneric = new GenericClass<Integer>(1);
同样泛型的编辑也可以作用于泛型方法:
static void printGeneric(GenericClass<?> item) {
System.out.print("printGeneric :" + item.getDescription() + " \r\n");
}
泛型擦除
观察下面一组代码:
public static void main(String[] args) {
// GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
// GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
// System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
// System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
// String str = createT(String.class);
// GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
// GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
// printGeneric(numberGeneric);
// printGeneric(integerGeneric);
// GenericTest gt = new GenericTest();
// printGeneric(Male.class, gt);
// printGeneric(Female.class, gt);
GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
GenericClass<Integer> integerGeneric = new GenericClass<Integer>(100);
System.out.print("String :" + stringGeneric.getClass() + " \r\n");
System.out.print("Integer:" + integerGeneric.getClass() + " \r\n");
String str = stringGeneric.get();
Integer integer = integerGeneric.get();
System.out.print("str:" + str + " integer:" + integer);
}
static class GenericClass<T> {
T mField;
GenericClass(T field) {
mField = field;
}
T get() {
return mField;
}
String getDescription() {
return "Class is " + mField.getClass() + " values is " + mField;
}
}
输出结果为:
String class GenericTest$GenericClass
Integer class GenericTest$GenericClass
str:ABC integer:100
从输出结果看无论类型参数如何传输,泛型的类都是一样的,我们从字节码来观察下,main函数字节码如下:
public static void main(java.lang.String[]);
Code:
0: new #2 // class GenericTest$GenericClass
3: dup
4: ldc #3 // String ABC
6: invokespecial #4 // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
9: astore_1
10: new #2 // class GenericTest$GenericClass
13: dup
14: bipush 100
16: invokestatic #5 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
19: invokespecial #4 // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
22: astore_2
23: aload_1
24: invokevirtual #6 // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
27: checkcast #7 // class java/lang/String
30: astore_3
31: aload_2
32: invokevirtual #6 // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
35: checkcast #8 // class java/lang/Integer
38: astore 4
40: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
43: new #10 // class java/lang/StringBuilder
46: dup
47: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
50: ldc #12 // String str:
52: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
55: aload_3
56: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
59: ldc #14 // String integer:
61: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
64: aload 4
66: invokevirtual #15 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
69: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
72: invokevirtual #17 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
75: return
可以看到 24行调用get方法返回类型为Object,第27行将Object转换成String。同样32行和35行也有类似的操作。
泛型数组
Java中不允许声明泛型数组,即下述写法是非法的:
List<String>[] ls = new ArrayList<String>[10];
但是可以通过通配符声明:
List<?>[] ls = new ArrayList<?>[10];
具体的原因Sun官网的一篇文章以及本文的参考文档上都有有介绍
网友评论