Java编程思想笔记0x09

泛型(四)

动态类型安全

  • 因为可以向旧版本的Java代码中传递容器,而如果旧版本的代码没有使用泛型则需要动态类型检查。java.uitl.Collections中有一系列方法可以完成这项工作:checkedCollection()checkedList()checkedMap()checkedSet()checkedSortedMap()checkedSortedSet()。其参数为希望获得检查的容器和强制要求的类型。受检查的容器在插入不正确的类型时会抛出ClassCastException。例如:

    List<Cat> cats = Collections.checkedList(new ArrayList<>(), Cat.class);

    此时插入Cat对象是正确的,而插入Dog对象则会出现异常。

异常

  • 由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接或间接继承Throwable
  • 在方法的throw子句可以使用类型参数,使得随检查异常的类型发生变化而变化的代码

混型

  • 混型是指回合多个类的能力,以产生一个可以表示混型中所有类型的类。
  • 混型的价值之一是可以将特性和行为一致地应用于多个类型,如果在混型类中修改,那么这些修改将会应用于混型所应用的所有类型之上。混型有一点面向切面编程的意思。

与接口混合

  • 一种常见的混型方法就是使用接口产生混型效果,即使用代理,每个混入类型都有一个相应的域,调用方法时要转发给对应的域。

使用装饰器模式

  • 装饰器是通过使用组合和形式化结构(可装饰物/装饰器层器结构)类实现的,而混型是基于继承的。因此可以将基于参数化类型的混型当做一种泛型装饰器机制,这种机制不需要装饰器设计模式的继承结构。(即声明一个混型,其泛型类型参数为混型基于的所有类型,以达到混型的目的

与动态代理混合

  • 可以使用动态代理实现更为接近混合模型的机制。通过使用动态代理,所产生的类的动态类型将会是已经混入的组合类型。
  • 由于动态代理的限制,每个被混入的类都必须是某个接口的实现。
  • 在调用混入类型的方法之前,需对混型对象进行向下转型到合适的类型。

潜在类型机制

  • 潜在类型机制,又称鸭子类型机制,具有这种机制的语言只要求实现某个方法的自己,而不是某个特定类型或者接口,从而放松了限制。潜在类型机制可以横跨类型继承结构,调用某个不是公共接口的方法。
  • 在Java中是没有原生实现的潜在类型机制,会被强制要求继承某个类或者实现某个接口。

对缺乏潜在类型机制的补救

反射

class CommunicateReflectively {
    public static void perform(Object speaker) {
        Class<?> spkr = speaker.getClass();
        try {
            Method speak = spkr.getMethod("speak");
            speak.invoke(speaker);
        } catch(NoSuchMethodException e) {
            System.out.println(speaker + "cannot speak");
        }
    }
}

上面的代码使用了反射获取了指定名称的方法,但是类型检查转移到了运行时。

将一个方法应用于序列

public class Apply {
    public static <T, S extends Iterable<? extends T>> 
        void apply(S seq, Method f, Object... args) {
        try {
            for (T t: seq) {
                f.invoke(t, args);
            }
        } catch(Exception e) {
            throw new RuntimException(e);
        }
    }
}
  • 上面代码的目的是实现对任意的序列S<T>调用某种方法,然而恰好序列都是实现了Iterable接口的。但是这种方法并不适用于没有实现内建接口或者继承内建类的类型。

用适配器模式仿真潜在类型机制

  • 可以使用适配器模式来适配已有的接口,来产生需要的接口。

数组

数组的特点

  • 效率:数组是一种效率最高的存储和随机访问对象引用序列的方式,代价是数组对象的大小被固定,并且在其生命周期中不能改变。
  • 类型:相比缺少泛型的容器,数组可以持有某种类型的对象,可以通过编译期检查来防止不当的操作。
  • 持有基本类型:数组可以持有基本类型,但是容器不能直接持有基本类型。
Author: SinLapis
Link: http://sinlapis.github.io/2019/07/07/Java编程思想笔记0x09/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.