第 12章
泛型( Generics)
– 泛型入门
– 泛型进阶语法没有泛型之前
public class BooleanFoo {
private Boolean foo;
public void setFoo(Boolean foo) {
this.foo = foo;
}
public Boolean getFoo() {
return foo;
}
} public class IntegerFoo {
private Integer foo;
public void setFoo(Integer foo) {
this.foo = foo;
}
public Integer getFoo() {
return foo;
}
}
没有泛型之前
public class ObjectFoo {
private Object foo;
public void setFoo(Object foo) {
this.foo = foo;
}
public ObjectgetFoo() {
return foo;
}
}
Object为最上层的父类别,所以用它来实现泛型( Generics)功能
转换型态时用错了型态没有泛型之前
ObjectFoo foo1 = new ObjectFoo();
ObjectFoo foo2 = new ObjectFoo();
foo1.setFoo(new Boolean(true));
//记得转换实现类型
Boolean b =(Boolean) foo1.getFoo();
foo2.setFoo(new Integer(10));
//记得转换实现类型
Integer i =(Integer) foo2.getFoo();
ObjectFoo foo1 = new ObjectFoo();
foo1.setFoo(new Boolean(true));
String s = (String) foo1.getFoo(); ClassCastException
定义泛型类别
J2SE5.0之后,针对泛型( Generics)设计的解决方案
使用 <T>用来声明一个型态持有者名称 T
public class GenericFoo<T> {
private T foo;
public void setFoo(T foo) {
this.foo = foo;
}
public T getFoo() {
return foo;
}
}
定义泛型类别
可以使用角括号一并指定泛型类别型态持有者 T真正的型态
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
foo1.setFoo(new Boolean(true));
Boolean b = foo1.getFoo(); //不需要再转换型态
System.out.println(b);
foo2.setFoo(new Integer(10));
Integer i = foo2.getFoo(); //不需要再转换型态
System.out.println(i);
定义泛型类别
型态或接口转换不再需要
– 省去恼人的 ClassCastException发生
编译程序可以帮您作第一层防线
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
foo1.setFoo(new Boolean(true));
Integer i = foo1.getFoo(); //传回的是 Boolean型态
GenericFooDemo.java:7,incompatible types
found,java.lang.Boolean
required,java.lang.Integer
Integer i = foo1.getFoo();
定义泛型类别
声明及配置对象时不一并指定型态,默认会使用 Object型态
编译时编译程序会提出警讯
GenericFoo foo3 = new GenericFoo();
foo3.setFoo(new Boolean(false));
Note,GenericFooDemo.java uses unchecked or unsafe operations.
Note,Recompile with -Xlint:unchecked for details.
定义泛型类别
GenericFoo< Boolean>声明的 foo1与
GenericFoo< Integer>声明的 foo2是不同的
不可以将 foo1所参考的实例指定给 foo2,或是将 foo2所参考的实例指定 給 foo1
GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>();
GenericFoo<Integer> foo2 = new GenericFoo<Integer>();
incompatible types
found,GenericFoo<java.lang.Integer>
required,GenericFoo<java.lang.Boolean>
foo1 = foo2;
几个定义泛型的例子
类上声明两个类型持有者 T1与 T2
public class GenericFoo2<T1,T2> {
private T1 foo1;
private T2 foo2;

}
GenericFoo<Integer,Boolean> foo =
new GenericFoo<Integer,Boolean>();
几个定义泛型的例子
可以用于声明数组型态
public class GenericFoo3<T> {
private T[] fooArray;
public void setFooArray(T[] fooArray) {
this.fooArray = fooArray;
}
public T[] getFooArray() {
return fooArray;
}
}
String[] strs = {"caterpillar","momor","bush"};
GenericFoo3<String> foo = new GenericFoo3<String>();
foo.setFooArray(strs);
strs = foo.getFooArray();
几个定义泛型的例子
可以使用泛型机制来声明一个数组
不可以使用泛型来建立数组的实例
public class GenericFoo<T> {
private T[] fooArray;
//,..
}
public class GenericFoo<T> {
private T[] fooArray = new T[10]; //不可以使用泛型建立数组实例
//,..
}
几个定义泛型的例子
想要设计一个新的类别,当中包括了范例
12.4的类别实例作为其成员
public class WrapperFoo<T> {
private GenericFoo<T> foo;
public void setFoo(GenericFoo<T> foo) {
this.foo = foo;
}
public GenericFoo<T> getFoo() {
return foo;
}
}
几个定义泛型的例子
GenericFoo<Integer> foo = new GenericFoo<Integer>();
foo.setFoo(new Integer(10));
WrapperFoo<Integer> wrapper = new WrapperFoo<Integer>();
wrapper.setFoo(foo);
限制泛型可用类型
一并使用 "extends"指定这个型态持有者实例化时,实例化的对象必须是扩充自某个类型或实作某接口
import java.util.List;
public class ListGenericFoo<T extends List> {
private T[] fooArray;
public void setFooArray(T[] fooArray) {
this.fooArray = fooArray;
}
public T[] getFooArray() {
return fooArray;
}
}
限制泛型可用类型
在限定持有者时,无论是要限定的对象是接口或类别,都是使用 "extends"关键词
ListGenericFoo<LinkedList> foo1 =
new ListGenericFoo<LinkedList>();
ListGenericFoo<ArrayList> foo2 =
new ListGenericFoo<ArrayList>();
限制泛型可用类型
如果不是实作 List的类别,编译时就会发生错误
ListGenericFoo<HashMap> foo3 =
new ListGenericFoo<HashMap>();
type parameter java.util.HashMap is not within its bound
ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();
限制泛型可用类型
您定义泛型类别时如果只写以下的话
相当于以下的定义方式
public class GenericFoo<T> {
//....
}
public class GenericFoo<T extends Object> {
//....
}
型态通配字符( Wildcard)
假设使用 GenericFoo类别来如下声明名称
下面的方式是可行的
GenericFoo<Integer> foo1 = null;
GenericFoo<Boolean> foo2 = null;
foo1 = new GenericFoo<Integer>();
foo2 = new GenericFoo<Boolean>();
型态通配字符( Wildcard)
可以使用‘?’「通配字符」( Wildcard),
‘?’代表未知型态,并使用,extends”关键词来作限定
以下这行无法通过编译
GenericFoo<? extends List> foo = null;
foo = new GenericFoo<ArrayList>();
.....
foo = new GenericFoo<LinkedList>();
....
GenericFoo<? extends List> foo = new GenericFoo<HashMap>();
型态通配字符( Wildcard)
编译程序会回报以下的错误
如果您不希望任何的型态都可以传入
showFoo()方法中
incompatible types
found,GenericFoo<java.util.HashMap>
required,GenericFoo<? extends java.util.List>
GenericFoo<? extends List> foo = new GenericFoo<HashMap>();
public void showFoo(GenericFoo<? extends String> foo) {
//针对 String或其子类而制定的内容,例如下面这行
System.out.println(foo.getFoo());
}
型态通配字符( Wildcard)
透过使用通配字符声明的名称所参考的对象,您没办法再对它加入新的信息,您只能取得它当中的信息或是移除当中的信息
GenericFoo<String> foo = new GenericFoo<String>();
foo.setFoo("caterpillar");
GenericFoo<?> immutableFoo = foo;
//可以取得信息
System.out.println(immutableFoo.getFoo());
//可透过 immutableFoo来移去 foo所参考实例内的信息
immutableFoo.setFoo(null);
//不可透过 immutableFoo来设定新的信息给 foo所参考的实例
//所以下面这行无法通过编译
// immutableFoo.setFoo("良葛格 ");
型态通配字符( Wildcard)
因为您不知道 <?>或是 <? extends
SomeClass>声明的参考名称,实际上参考的对象,当中确实储存的是什么类型的信息
基于泛型的设计理念,当然也就没有理由能加入新的信息了
因为若能加入,被加入的对象同样也会有失去型态信息的问题型态通配字符( Wildcard)
也可以向上限制,只要使用 "super"关键词
GenericFoo<? super StringBuilder> foo = null;
扩充泛型类别、实现泛型接口
public class SubGenericFoo4<T1,T2,T3>
extends GenericFoo4<T1,T2> {
private T3 foo3;
public void setFoo3(T3 foo3) {
this.foo3 = foo3;
}
public T3 getFoo3() {
return foo3;
}
}
可以扩充一个泛型类别,保留其型态持有者,并新增自己的型态持有者扩充泛型类别、实作泛型接口
如果不保留型态持有者,则继承下来的 T1
与 T2自动变为 Object
– 建议是父类别的型态持有者都要保留
界面实作
public class ConcreteFoo<T1,T2> implements IFoo<T1,T2> {
private T1 foo1;
private T2 foo2;

}