泛型也可以应用于接口。例如生成器,这是一种专门负责创建对象的类。实际上,这是工厂方法设计模式的一种应用。不够,当使用生成器创建新的对象的时候,它不需要任何参数,而工厂方法一般需要参数。也就是说,生成器无需额外的信息就知道如何创建新对象。
一般而言,一个生成器制定以一个方法,以该方法用以产生新的对象。在这里,就是next方法。
public interface Generator<T> {
T next();
}
方法next()的返回函数是参数化的T。正如你所见到的,接口使用泛型与类使用泛型没有什么区别。
为了演示如何实现Generator接口,我们还需要一些别的类,例如,Coffee类的层次结构如下:
public class Coffee {
private static long counter=0;//计数器,来计算已成生coffee实例的数量
private final long id=counter++;//id,final类型,当初始化之后,不可变,记录此实例对象的ID
public String toString(){
return getClass().getSimpleName()+""+id;
}
}
public class Mocha extends Coffee{
}
public class Cappuccino extends Coffee{
}
public class Latte extends Coffee{
}
现在我们编写一个类,实现Generator<Coffee>接口,他能够随机生成不同类型的Coffee对象:
public class CoffeeGenerator implements Generator<Coffee>,Iterable<Coffee>{
private Class[] types={Latte.class,Mocha.class,Cappuccino.class};
private static Random rand=new Random(47);
public CoffeeGenerator(){};
private int size=0;
public CoffeeGenerator(int size){
this.size=size;
}
@Override
public Coffee next() {
// TODO Auto-generated method stub
try {
return (Coffee)types[rand.nextInt(types.length)].newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException();
}
}
class CoffeeIterator<Coffee> implements Iterator{
int count=size;
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return count>0;
}
@Override
public Object next() {
// TODO Auto-generated method stub
count--;
return CoffeeGenerator.this.next();
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}
@Override
public Iterator<Coffee> iterator() {
// TODO Auto-generated method stub
return new CoffeeIterator();
}
public static void main(String[] args) {
CoffeeGenerator cg=new CoffeeGenerator();
for(int i=0;i<5;i++){
System.out.println(cg.next());
}
for(Coffee c:new CoffeeGenerator(5)){
System.out.println(c);
}
}
}
参数化的Generator接口确保next()函数返回值是参数的类型。CoffeeGenerator同时还实现了Iterable接口,所以他可以在循环语句中使用(foreach语法详见http://blog.csdn.net/fxkcsdn/article/details/76576652)。不过,它还需要一个末端哨兵来判断何时停止,这正是第二个构造器的功能。
下面的类是Generator<T>接口的另一个实现,它负责生成Fibonacci数列:
public class Fibonacci implements Generator<Integer>{
private int count=0;
@Override
public Integer next() {
// TODO Auto-generated method stub
return fbc(count++);
}
public int fbc(int n){
if(n<2){
return 1;
}else{
return fbc(n-1)+fbc(n-2);
}
}
public static void main(String[] args) {
Fibonacci fc=new Fibonacci();
for(int i=0;i<10;i++){
System.out.println(fc.next());
}
}
}
虽然我们在Fibonacci类的里里外外使用的都是int类型,但是其类型参数确实Integer.这个例子引出了Java泛型的一个局限性:基本类型无法作为类型参数。不过,javaSE5具备了自动打包和自动拆包的功能,可以很方便的在基本类型和其对应的包装器类型之间进行转换。通过这个例子中的Fibonacci类对Int的使用,我们已经看到了这种效果。
如果还想更近一步,编写一个实现Iterable的FIbonacci生成器。我们的一个选择是充血这个类,令其实现Iterable接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个适配器。来实现所需的接口。
有多种方法实现适配器。例如,可以通过继承来创建适配器:
public class IterableFibonacci extends Fibonacci implements Iterable<Integer>{
private int size;
public IterableFibonacci(int size){
this.size=size;
}
@Override
public Iterator<Integer> iterator() {
// TODO Auto-generated method stub
return new Iterator<Integer>(){
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return size>0;
}
@Override
public Integer next() {
// TODO Auto-generated method stub
size--;
return IterableFibonacci.this.next();
}
@Override
public void remove() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
};
}
public static void main(String[] args) {
IterableFibonacci ifc=new IterableFibonacci(10);
for(Integer i:ifc){
System.out.println(i);
}
}
}
如果要在循环语句中使用IterableFibonacci,必须向IterableFinacci的构造器提供一个边界值,然后hasnext()方法才能知道何时返回false.