本来这部分的内容是相对简单的,但是在看书的过程中发现了一个很好的例子,涉及了好几个知识点,忍不住要写一篇博文。
泛型接口,顾名思义,泛型也可以应用于接口,接口使用泛型和类使用泛型没什么区别。下面举一个生成器的例子,是工厂方法的一种应用,负责创建对象的类。
生成器接口:
//接口方法返回类型是参数化的T,切记要与泛型方法进行区别;泛型方法是为了参数的泛化,后面我们将会给出一个简单的实例
public interface Generator<T>{ T next();}
实体类结构:
public class Coffee {
private static long counter = 0;
private final long id = counter++;
public String toString() {
return getClass().getSimpleName() + " " + id;
}
}
class Latte extends Coffee {}
class Mocha extends Coffee {}
class Cappuccino extends Coffee {}
class Breve extends Coffee {}
class Americano extends Coffee {}
接口实现,目的是随机生成对象
public class CoffeeGenerator implements Generator<Coffee>, Iterable<Coffee> {
private Class[] types = {Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class};
private static Random rand = new Random(47);
public CoffeeGenerator() {}
private int size = 0;
//构建末端哨兵
public CoffeeGenerator(int sz) { size = sz; }
//利用反射机制,随机返回对象实例
public Coffee next() {
try {
return (Coffee)types[rand.nextInt(types.length)].newInstance();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
//迭代器类的具体实现
class CoffeeIterator implements Iterator<Coffee> {
int count = size;
public boolean hasNext() { return count > 0; }
public Coffee next() {
count--;
return CoffeeGenerator.this.next();
}
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
//返回一个迭代器对象
public Iterator<Coffee> iterator() {
return new CoffeeIterator();
}
public static void main(String[] args) {
CoffeeGenerator gen = new CoffeeGenerator();
for(int i = 0; i < 5; i++){
System.out.println(gen.next());
}
for(Coffee c : new CoffeeGenerator(5)){
System.out.println(c);
}
}
}
当然,在这里不能给出确定的输出,因为输出结果是随机产生的。
进一步地,我们可以尝试用匿名内部类来改进iterator()方法:
public Iterator<Coffee> iterator() {
return new Iterator<Coffee> {
int count = size;
public boolean hasNext() { return count > 0; }
public Coffee next() {
count--;
return CoffeeGenerator.this.next();
}
public void remove() { // Not implemented
throw new UnsupportedOperationException();
}
};
}
该例子涉及的知识点:
泛型接口:正如前面说所,接口的泛型和类的泛型使用是没有什么区别的。但需要注意的是,同时也是Java泛型的一个局限:基本类型无法作为类型参数。如果你需要使用它时,必须使用基本类型对应的包装器类,包装器类会自动进行打包拆包,所以我们正常使用即可。
迭代器:这里想重点强调的是
Iterable
与Iterator
的用法,如果我们刚开始接触,很容易混淆这两个接口的的区别和用途。在这个例子中我们可以很好的理解他们的用途,Iterable
接口需要实现iterator()
方法,而iterator()
方法返回的是一个Iterator
类型的迭代器对象,所以真正实现迭代功能的是CoffeeIterator
。类实现了Iterable
接口,使得它可以在foreach
循环语句中使用。反射机制:
(Coffee)types[rand.nextInt(types.length)].newInstance()
在运行时确定创建的实例类型,在一定程度上这里就是一个生产Coffee
的工厂。匿名内部类
为了区别返回类型为泛型的方法和泛型方法,举一个简单的对比实例:
返回类型为泛型的方法:
public interface Generator<T>{
T next();
}
泛型方法:
public interface Generator{
public <T> void f(T x);
}
泛型方法主要是为了在方法参数中使用泛型,是否拥有泛型方法,与其所在的类是否是泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。
使用泛型方法的原则:
- 如果通过泛型方法能够实现的事情,就尽量使用泛型方法而避免使用整个类型的泛化。
- 对于一个static的方法,无法访问泛型类型的类型参数。如果static方法需要使用泛型能力,就必须使其成为泛型方法。
public class Generator<T>{
//当然如果是接口的方法,那每个方法都是public static
public static <T> void f(T x);
}
- 当使用泛型类型时,必须在创建对象的时候指定类型参数的值;而使用泛型方法时候,通常不必指明参数类型。
到这里这个实例就基本讲完了,一下子写了这么些知识点,得好好消化消化了。
参考:
- 《Thinking in Java》