🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### [标记接口](https://lingcoder.gitee.io/onjava8/#/book/19-Type-Information?id=%e6%a0%87%e8%ae%b0%e6%8e%a5%e5%8f%a3) 有时候使用一个**标记接口**来表示空值会更方便。标记接口里边什么都没有,你只要把它的名字当做标签来用就可以。 ~~~ // onjava/Null.java package onjava; public interface Null {} ~~~ 如果你用接口取代具体类,那么就可以使用`DynamicProxy`来自动地创建`Null`对象。假设我们有一个`Robot`接口,它定义了一个名字、一个模型和一个描述`Robot`行为能力的`List<Operation>`: ~~~ // typeinfo/Robot.java import onjava.*; import java.util.*; public interface Robot { String name(); String model(); List<Operation> operations(); static void test(Robot r) { if (r instanceof Null) System.out.println("[Null Robot]"); System.out.println("Robot name: " + r.name()); System.out.println("Robot model: " + r.model()); for (Operation operation : r.operations()) { System.out.println(operation.description.get()); operation.command.run(); } } } ~~~ 你可以通过调用`operations()`来访问`Robot`的服务。`Robot`里边还有一个`static`方法来执行测试。 `Operation`包含一个描述和一个命令(这用到了**命令模式**)。它们被定义成函数式接口的引用,所以可以把 lambda 表达式或者方法的引用传给`Operation`的构造器: ~~~ // typeinfo/Operation.java import java.util.function.*; public class Operation { public final Supplier<String> description; public final Runnable command; public Operation(Supplier<String> descr, Runnable cmd) { description = descr; command = cmd; } } ~~~ 现在我们可以创建一个扫雪`Robot`: ~~~ // typeinfo/SnowRemovalRobot.java import java.util.*; public class SnowRemovalRobot implements Robot { private String name; public SnowRemovalRobot(String name) { this.name = name; } @Override public String name() { return name; } @Override public String model() { return "SnowBot Series 11"; } private List<Operation> ops = Arrays.asList( new Operation( () -> name + " can shovel snow", () -> System.out.println( name + " shoveling snow")), new Operation( () -> name + " can chip ice", () -> System.out.println(name + " chipping ice")), new Operation( () -> name + " can clear the roof", () -> System.out.println( name + " clearing roof"))); public List<Operation> operations() { return ops; } public static void main(String[] args) { Robot.test(new SnowRemovalRobot("Slusher")); } } ~~~ 输出结果: ~~~ Robot name: Slusher Robot model: SnowBot Series 11 Slusher can shovel snow Slusher shoveling snow Slusher can chip ice Slusher chipping ice Slusher can clear the roof Slusher clearing roof ~~~ 假设存在许多不同类型的`Robot`,我们想让每种`Robot`都创建一个`Null`对象来执行一些特殊的操作——在本例中,即提供`Null`对象所代表`Robot`的确切类型信息。这些信息是通过动态代理捕获的: ~~~ // typeinfo/NullRobot.java // Using a dynamic proxy to create an Optional import java.lang.reflect.*; import java.util.*; import java.util.stream.*; import onjava.*; class NullRobotProxyHandler implements InvocationHandler { private String nullName; private Robot proxied = new NRobot(); NullRobotProxyHandler(Class<? extends Robot> type) { nullName = type.getSimpleName() + " NullRobot"; } private class NRobot implements Null, Robot { @Override public String name() { return nullName; } @Override public String model() { return nullName; } @Override public List<Operation> operations() { return Collections.emptyList(); } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(proxied, args); } } public class NullRobot { public static Robot newNullRobot(Class<? extends Robot> type) { return (Robot) Proxy.newProxyInstance( NullRobot.class.getClassLoader(), new Class[] { Null.class, Robot.class }, new NullRobotProxyHandler(type)); } public static void main(String[] args) { Stream.of( new SnowRemovalRobot("SnowBee"), newNullRobot(SnowRemovalRobot.class) ).forEach(Robot::test); } } ~~~ 输出结果: ~~~ Robot name: SnowBee Robot model: SnowBot Series 11 SnowBee can shovel snow SnowBee shoveling snow SnowBee can chip ice SnowBee chipping ice SnowBee can clear the roof SnowBee clearing roof [Null Robot] Robot name: SnowRemovalRobot NullRobot Robot model: SnowRemovalRobot NullRobot ~~~ 无论何时,如果你需要一个空`Robot`对象,只需要调用`newNullRobot()`,并传递需要代理的`Robot`的类型。这个代理满足了`Robot`和`Null`接口的需要,并提供了它所代理的类型的确切名字。