动态类型安全
1.Java SE5中的java.util.Collections中有一组工具用于检查容器所持有的类型是否是我们所需要的,它们是:静态方法checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()、checkedSortedSet()。这些方法会将你希望动态检查的容器当作第一个参数接受,并将你希望强制要求的类型作为第二个参数接受。
List<Dog> dogs = Collections.checkedList(new ArrayList<Dog>(), Dog.class);
2.因为可以向Java SE5之前的代码传递泛型容器,所以旧式代码仍旧有可能破坏你的容器,此时上述工具就可以解决在这种情况下的类型检查问题。
异常
1.由于擦除的原因,嫁给你泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接货或间接继承自Throwable。
混型
1.混型的最基本的概念是混合多个类的能力。以产生一个可以表示混型中所有与类型的类。
一.与接口混合
1.一种常见的产生混型效果的方法是使用接口:
interface TimeStamped {
long getStamp();
}
class TimeStampedImp implements TimeStamped {
private final long timeStamp;
public TimeStampedImp() {
timeStamp = new Date().getTime();
}
public long getStamp() {
return timeStamp;
}
}
interface SerialNumbered {
long getSerialNumbered();
}
class SerialNumberedImp implements SerialNumbered {
private static long counter = 1;
private final long serialNumber = counter++;
private long getSerialNumber() {
return serialNumber;
}
}
interface Basic {
public void set(String val);
public String get();
}
class BasicImp implements Basic {
private String value;
public void set(String val) {
this.value = val;
}
public String get() {
return value;
}
}
class Mixmin extends BasicImp implements TimeStamped, SerialNumbered {
private TimeStamped timeStamp = new TimeStampedImp();
private SerialNumbered serialNumber = new SerialNumberedImp();
public long getStamp() {
return timeStamp.getStamp();
}
public long getSerialNumber() {
return serialNumber.getSerialNumber();
}
}
public class Mixmins {
public static void main (String... args) {
Mixmin mixmin1 = new Mixmin(), mixmin2 = new Mixmin();
mixmin1.set("string1");
mixmin2.set("string1");
System.out.println(mixmin1.get() + " " + mixmin1.getStamp() + " " + mixmin1.getSerialNumber());
System.out.println(mixmin2.get() + " " + mixmin2.getStamp() + " " + mixmin2.getSerialNumber());
}
}
这个示例的使用方法非常简单,但是当使用更加复杂的混型时,代码量会急速增加。
二.使用装饰器模式
1.装饰器是通过使用组合和形式化结构来实现的,而混型时基于继承的。因此可以将基于参数化类型的混型当作一种泛型装饰器机制,这种机制不需要装饰器设计模式的继承结构:
class Basic {
private String value;
public void set(String val) {
this.value = val;
}
public String get() {
return value;
}
}
class Decorator extends Basic {
protected Basic basic;
public Decorator(Basic basic) {
this.basic = basic;
}
public void set(String val) {
basic.set(val);
}
public String get() {
return basic.get();
}
}
class TimeStamped extends Decorator {
private final long timeStamp;
public TimeStampedImp(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);
}
private long getSerialNumber() {
return serialNumber;
}
}
public class Decoration {
public static void main(String...args) {
TimeStamped t = new TimeStamped(new Basic());
TimeStamped t2 = new TimeStamped(new SerialNumbered(new Basic()));
SerialNumbered t = new SerialNumbered(new Basic());
SerialNumbered t2 = new SerialNumbered(new TimeStamped(new Basic()));
}
}
对于装饰器来说,其明显的缺陷谁它只能有效地工作于装饰中的最后一层,而混型方法显然会更佳自然一些,因此,装饰器只是对由混型提出的问题的一种局限的解决方案。
三.与动态代理结合
1.可以使用动态代理来创建一种比装饰器更贴近混型模型的机制。由于动态代理的限制,每个被混入的类都必须时某个接口的实现:
class MixminProxy implements InvocationHandler {
Map<String, Object> delegatesByMethod;
public MixminProxy(TwoTuple<Object, Class<?>>... pairs) {
delegatesByMethod = new HashMap<String, Object>();
for (TwoTuple<Object, Class<?>> pair : pairs) {
for (Method method : pair.second.getMethods()) {
String methodName = method.getName();
if (!delegatesByMethod.containsKey(methodName))
delegatesByMethod.put(methodName, pair.first);
}
}
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Object delegate = delegatesByMethod.get(methodName);
return method.invoke(delegate, args);
}
public static Object newInstance(TwoTuple... pairs) {
Class[] interfaces = new Class[pairs.length];
for (int i = 0; i < pairs.length; i ++) {
interfaces[i] = (Class) pairs[i].second;
}
ClassLoader cl = pair[0].first.getClassLoader();
return Proxy.newProxyInstance(cl, interfaces, new MixminProxy(pairs));
}
}
public class DynamicProxyMixmin {
public static void main(String... args) {
Object mixmin = MixminProxy.newInstance(
tuple(new BasicImp(), Basic.class),
tuple(new TimeStampedImp(), TimeStamped.class),
tuple(new SerialNumberedImp(), SerialNumbered.class));
Basic b = (Basic) mixmin;
TimeStamped t = (TimeStamped) mixmin;
SerialNumbered s = (SerialNumbered) mixmin;
b.set("Hello");
System.out.println(b.get());
System.out.println(t.get());
System.out.println(s.get());
}
}
这种方案要比上面两种方式更加接近于真正的混型。
潜在类型机制
1.某些编程语言提供了一种机制——潜在类型机制。
2.泛型代码典型地将在泛型类型上调用少量方法,而具有潜在类型机制的语言只要求实现某个方法的子集,而不是某个特定类或接口,从而放松了这种限制。
3.潜在类型机制是一种代码组织和复用机制。有了它编写出来的代码相对于没有它编写出的代码,能够更容易滴复用。
4.由于泛型是后期才加进Java的,因此没有任何机会可以去实现任何类型的潜在类型机制,因此Java没有对这种类型的支持。
对缺乏潜在类型机制的补偿
一.反射
在Java中使用潜在类型机制,可以使用的一种方式是反射,下面的perform()方法就是用了潜在类型机制:
class Mime {
public void walkAgainstTheWind() {}
public void sit() { print("Pretending to sit"); }
public void pushInvisibleWalls() {}
public String toString() { return "Mime"; }
}
class SmartDog {
public void speak() { print("Woof!"); }
public void sit() { print("Sittint"); }
public void reproduce() {}
}
class CommunicateReflectively {
public static void perform(Object speaker) {
Class<?> spkr = speaker.getClass();
try {
try {
Method speak = spkr.getMethod("speak");
speak.invoke(speaker);
} catch (NoSuchMethodException e) {
print(speaker + "cannot speak");
}
try {
Method sit = spkr.getMethod("sit");
sit.invoke(sit);
} catch (NoSuchMethodException e) {
print(speaker + "cannot sit");
}
} catch (Exception e) {
throw new RuntimeException(speaker.toString(), e);
}
}
}
public class LatentReflection {
public static void main(String.... args) {
CommunicateReflectively.perform(new SmartDog());
CommunicateReflectively.perform(new Robot());
CommunicateReflectively.perform(new Mime());
}
}
上述代码中,这些类都是彼此分离的。
将一个方法应用于序列
1.反射虽然提供了潜在类型机制的可能性,但是它将所有类型检查都转移到了运行时。如果能够实现编译期类型检查,这通常会更加符合要求。
2.假设想要创建一个方法,它能够将任何方法应用于某个系列中的所有对象。我们可以使用上面的反射以及可变参数来解决这个问题:
class Shape {
public void rotate() { print(this + " rotate") }
public void resize(int newSize) {
print(this + " resize" + newSize);
}
}
class Square extends Shape {}
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 RuntimeException(e);
}
}
}
class FilledList<T> extends ArrayList<T> {
public FilledLIst(Class<? extends T> type, int size) {
try {
for (int i = 0; i < size; i ++) {
add(type.newInstance());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class Test {
public static void main(String... args) throws Exception {
List<Shape> shapes = new ArrayList<Shape>();
for (int i = 0; i < 10; i ++)
shapes.add(new Shape());
Apply.apply(shapes, Shape.class.getMethod("rotate"));
Apply.apply(shapes, Shape.class.getMethod("resize", int.class), 5);
Apply.apply(new FilledList<Shape>(Shape.class, 10), Shape.class.getMethod("rotate"));
}
}
尽管之中方法的解决方法背证明很优雅, 但是我们必须知道使用反射比非反射可能要慢一些,因为动作都是在运行时发生的。
三.当你并未碰巧拥有正确的接口时
1.如果具有潜在类型机制的参数化类型机制,你不会受任何特定类库的创建者过去所作的设计的支配,不想上面的代码需要适合需求的接口,因此这样的代码不是特别的“泛化”。
四.用适配器模仿潜在类型机制
1.实际上,潜在类型机制创建一个包含所需方法的隐式接口。因此它遵循这样的规则L如果我们手工编写了必需的接口,那么它就应该能够解决问题。
2.从我们拥有的接口中编写代码来产生我们需要的接口,这是适配器设计模式的一个典型示例。我们可以使用适配器来适配已有的接口,以产生想要的接口。