java编程思想读书笔记 第十五章 泛型(下)

来源:互联网 发布:php yaf文档 编辑:程序博客网 时间:2024/05/20 04:11

1.异常
由于擦除的原因,将泛型应用于异常时非常受限的。catch语句不能捕获泛型类型的异常,因为在编译器和运行时都必须知道异常的确类型。切泛型类也不能直接或间接继承自Throwable。但是,类型参数可能会在一个方法的throws子句中用到。例如:

interface Processor<T, E extends Exception> {    void process(List<T> resultCollector) throws E;}class ProcessRunner<T, E extends Exception> extends ArrayList<Processor<T, E>> {    List<T> processAll() throws E {        List<T> resultCollector = new ArrayList<T>();        for (Processor<T, E> processor : this) {            processor.process(resultCollector);        }        return resultCollector;    }}class Failure1 extends Exception {}class Processor1 implements Processor<String, Failure1> {    static int count = 3;    public void process(List<String> reList) throws Failure1 {        if (count-- > 1) {            reList.add("Hep!");        } else {            reList.add("Ho!");        }        if (count < 0) {            throw new Failure1();        }    }}class Failure2 extends Exception {}class Processor2 implements Processor<Integer, Failure2> {    static int count = 2;    public void process(List<Integer> resultCollector) throws Failure2 {        if (count-- == 0) {            resultCollector.add(47);        } else {            resultCollector.add(11);        }        if (count < 0) {            throw new Failure2();        }    }}public class ThrowGenricException {    public static void main(String[] args) {        ProcessRunner<String, Failure1> runner1 = new ProcessRunner<String, Failure1>();        for (int i = 0; i < 3; i++) {            runner1.add(new Processor1());        }        try {            System.out.println(runner1.processAll());        } catch (Failure1 e) {            System.out.println(e);        }        ProcessRunner<Integer, Failure2> runner2 = new ProcessRunner<Integer, Failure2>();        for (int i = 0; i < 3; i++) {            runner2.add(new Processor2());        }        try {            System.out.println(runner2.processAll());        } catch (Failure2 e) {            System.out.println(e);        }    }}

Processor执行process(),并且可能会抛出具有类型E的异常。process()的结果存储在List resultCollector中(这被称为收集参数)。ProcessRunner有个processAll()方法,它将执行所持有的每个Processor对象,并返回resultCollector。如果不能参数化所抛出的异常,那么由于检查型参数的缘故,将不能编写出这种泛化的代码。

2.混型
术语混型随时间的推移有了无数的含义,但是其最基本的概念是混合多个类的能力,以产生一个可以表示混型中所有类型的类。混型的价值之一是它们可以将特性和行为一致地应用多个类之上。如果想在混型中修改某些东西,这些修改将会应用于混型所应用的所有类型之上。
混型的三种方案:一是与接口混型;二是使用装饰器模式(装饰器模式是使用分层对象来动态透明地向单个对象中添加责任。装饰器是指定包装在最初的对象周围的所有对象都具有相同的基本接口。某些事物是可装饰的,可以通过将其他类包装在这个可装饰对象的四周,来将功能分层。这使得对装饰器的使用时透明的–无论对象是否被装饰,你都拥有一个可以向对象发送的公共消息集。装饰类也可以添加新的方法,但是这将时受限的。);三是与动态代理混合。下面举一个装饰器模式的例子:
装饰器是通过使用组合和形式化结构(可装饰物/装饰器层次结构)来实现的,而混型是基于继承的,因此可以将基于参数化类型的混型当作是一种泛型装饰机制,这种机制不需要装饰器设计模式的继承结构。代码如下:

class Basic{    private String value;    public String getValue() {        return value;    }    public void setValue(String value) {        this.value = value;    }}class Decorator extends Basic{    public Decorator(Basic basic){        this.basic = basic;    }    protected Basic basic;    public String getValue() {        return basic.getValue();    }    public void setValue(String value) {        basic.setValue(value);    }}class TimeStamped extends Decorator{    private final long timeStamp;    public TimeStamped (Basic basic) {        super(basic);        timeStamp = new Date().getTime();    }    public long getStamp() {        return timeStamp;    }}class SerialNumbered extends Decorator{    private static long counter = 1;    private final long serialNumber = counter++;    public SerialNumbered(Basic basic){        super(basic);    }    public long getSerialNumber() {        return serialNumber;    }}public class Decoretion {    public static void main(String[] args) {        TimeStamped t = new TimeStamped(new Basic());        TimeStamped t2 = new TimeStamped(new SerialNumbered(new Basic()));        SerialNumbered s = new SerialNumbered(new Basic());        SerialNumbered s1 = new SerialNumbered(new TimeStamped(new Basic()));    }}

产生自泛型的类包含所有感兴趣的方法,但是由使用装饰器所产生的对象类型是最后被装饰的类型。也就是说,尽管可以添加多个层,但是最后一层才是实际的类型,因此只有最后一层的方法是可视的,而混型的类型是所有被混合到一起的类型。因此对于装饰器来说,其明显的缺陷是它只能有效地工作与装饰中的一层(最后一层)。

3.潜在类型机制
泛型代码典型地将在泛型类型上调用少量的方法,而具有潜在类型机制的语言只要实现某个方法子集,而不是某个特定的类或接口,从而放松了限制。潜在类型机制使一种代码组织和复用机制。好处就是能够使代码更容易地复用,即编写一次,多次使用的效果。下面举个例子:

interface Performs{    void speak();    void sit();}class Dog {    public void speak(){}    public void sit(){}    public void reproduce(){}}class PerformingDog extends Dog implements Performs{    public void speak(){        System.out.println("Woof!");    }    public void sit(){        System.out.println("Sitting!");    }    public void reproduce(){}}class Robot implements Performs{    @Override    public void speak() {        System.out.println("Click!");    }    @Override    public void sit() {        System.out.println("Clank!");    }    public void oilChange(){}}class Communicate{    public static <T extends Performs> void perform(T perfomer) {        perfomer.speak();        perfomer.sit();    }}public class DogsAndRobots {    public static void main(String[] args) {        PerformingDog dog = new PerformingDog();        Robot robot = new Robot();        Communicate.perform(dog);        Communicate.perform(robot);    }}

在本例子中,泛型不是必须的,因为这些类已经被强制要求实现Performs接口了。这些类被要求使用Performs接口,并在边界表达式中指定它。

4.对缺乏潜在类型机制的补偿

(1)使用反射可以实现类似潜在类型机制的功能;但是我们知道使用反射可能比非反射的实现要慢一些,因为有太多的动作都是在运行时发生的。但是反射至少可以是一种解决方案,能够实现想要创建一个方法,它能将任何方法应用于某个系列中的所有对象,
(2)使用适配器模式。如果有一个具体类型而不是继承结构的基类,那么当你使用继承来创建适配器时,你可以稍微少些一些代码。使用适配器是对缺乏类型机制的一种补偿。

0 0