Java泛型接口、迭代器与反射机制结合实例,和泛型方法的使用原则

本来这部分的内容是相对简单的,但是在看书的过程中发现了一个很好的例子,涉及了好几个知识点,忍不住要写一篇博文。

泛型接口,顾名思义,泛型也可以应用于接口,接口使用泛型和类使用泛型没什么区别。下面举一个生成器的例子,是工厂方法的一种应用,负责创建对象的类。

生成器接口:

//接口方法返回类型是参数化的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泛型的一个局限:基本类型无法作为类型参数。如果你需要使用它时,必须使用基本类型对应的包装器类,包装器类会自动进行打包拆包,所以我们正常使用即可。

  • 迭代器:这里想重点强调的是IterableIterator的用法,如果我们刚开始接触,很容易混淆这两个接口的的区别和用途。在这个例子中我们可以很好的理解他们的用途,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》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值