zoukankan      html  css  js  c++  java
  • Java编程思想之十四 类型信息

    第十四章 类型信息

    运行时类型信息使得你可以在程序运行时发现和使用类型信息

    14.1 为什么需要RTTI

    面向对象编程中基本的目的是:让代码只操作对基类的引用。
    多态:

    import java.util.*;
    
    abstract class Shape {
      void draw() { System.out.println(this + ".draw()"); }
      abstract public String toString();
    }
    
    class Circle extends Shape {
      public String toString() { return "Circle"; }
    }
    
    class Square extends Shape {
      public String toString() { return "Square"; }
    }
    
    class Triangle extends Shape {
      public String toString() { return "Triangle"; }
    }	
    
    public class Shapes {
      public static void main(String[] args) {
        List<Shape> shapeList = Arrays.asList(
          new Circle(), new Square(), new Triangle()
        );
        for(Shape shape : shapeList)
          shape.draw();
      }
    } /* Output:
    Circle.draw()
    Square.draw()
    Triangle.draw()
    *///:~
    

    Rtti基本使用形式:所有类型转换都是在运行时进行正确性检查的。在运行时识别一个对象的类型。

    14.2 Class对象

    每当编写并且编译一个新类,就会产生一个Class对象。为了生成这个类的对象,运行这个程序的Java虚拟机(JVM)将使用被称为"类加载器"的子系统。
    类加载器子系统实际上可以包含一条加载器链,但是只有一个原生类加载器,它是JVM实现的一部分。原生类加载器加载的所谓的可信类,它们通常是从本地盘加载的。
    所有类都在第一个使用时,动态加载到JVM中。
    Java程序在它开始运行之前并非被完全加载,其各个部分是在必须时才加载的。
    类加载器首先检查这个类的class对象是否已经加载。如果没有加载,默认的类加载器就会根据类名查找.class文件。

    //: typeinfo/toys/ToyTest.java
    // Testing class Class.
    package toys;
    
    interface HasBatteries
    {
    }
    
    interface Waterproof
    {
    }
    
    interface Shoots
    {
    }
    
    class Toy
    {
        // Comment out the following default constructor
        // to see NoSuchMethodError from (*1*)
        Toy()
        {
        }
    
        Toy(int i)
        {
        }
    }
    
    class FancyToy extends Toy
            implements HasBatteries, Waterproof, Shoots
    {
        FancyToy()
        {
            super(1);
        }
    }
    
    public class ToyTest
    {
        static void printInfo(Class cc)
        {
            System.out.println("Class name: " + cc.getName() +
                    " is interface? [" + cc.isInterface() + "]");
            System.out.println("Simple name: " + cc.getSimpleName());
            System.out.println("Canonical name : " + cc.getCanonicalName());
        }
    
        public static void main(String[] args)
        {
            Class c = null;
            try
            {
                c = Class.forName("toys.FancyToy");
            }
            catch (ClassNotFoundException e)
            {
                System.out.println("Can't find FancyToy");
                System.exit(1);
            }
            printInfo(c);
            for (Class face : c.getInterfaces())
                printInfo(face);
            Class up = c.getSuperclass();
            Object obj = null;
            try
            {
                // Requires default constructor:
                obj = up.newInstance();
            }
            catch (InstantiationException e)
            {
                System.out.println("Cannot instantiate");
                System.exit(1);
            }
            catch (IllegalAccessException e)
            {
                System.out.println("Cannot access");
                System.exit(1);
            }
            printInfo(obj.getClass());
        }
    } /* Output:
    Class name: typeinfo.toys.FancyToy is interface? [false]
    Simple name: FancyToy
    Canonical name : typeinfo.toys.FancyToy
    Class name: typeinfo.toys.HasBatteries is interface? [true]
    Simple name: HasBatteries
    Canonical name : typeinfo.toys.HasBatteries
    Class name: typeinfo.toys.Waterproof is interface? [true]
    Simple name: Waterproof
    Canonical name : typeinfo.toys.Waterproof
    Class name: typeinfo.toys.Shoots is interface? [true]
    Simple name: Shoots
    Canonical name : typeinfo.toys.Shoots
    Class name: typeinfo.toys.Toy is interface? [false]
    Simple name: Toy
    Canonical name : typeinfo.toys.Toy
    *///:~
    

    14.2.1 类字面常量

    Java还提供了一个方法来生成对Class对象的引用,即使用类字面常量
    FancyToy.class;
    类字面常量不仅可以应用于普通的类,也可以应用于接口、数组、基本数据类型。
    对于基本数据类型的包装器类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象。
    当使用.class创建对象的引用时,不会自动初始化该class对象。
    为了使用类而做的准备工作实际包含三个步骤:

    1. 加载:这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个Class对象。
    2. 链接:在链接阶段将验证类中的字节码,为静态域分配存储空间,并且如果必须的话,将解析这个类创建的对其他类的所有引用。
    3. 初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块。
    //: typeinfo/ClassInitialization.java
    import java.util.*;
    
    class Initable {
      static final int staticFinal = 47;//编译期常量,不需要进行初始化就可以读取
      static final int staticFinal2 =
        ClassInitialization.rand.nextInt(1000);
      static {
        System.out.println("Initializing Initable");
      }
    }
    
    class Initable2 {
      static int staticNonFinal = 147;
      static {
        System.out.println("Initializing Initable2");
      }
    }
    
    class Initable3 {
      static int staticNonFinal = 74;
      static {
        System.out.println("Initializing Initable3");
      }
    }
    
    public class ClassInitialization {
      public static Random rand = new Random(47);
      public static void main(String[] args) throws Exception {
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        // Does not trigger initialization:
        System.out.println(Initable.staticFinal);
        // Does trigger initialization:
        System.out.println(Initable.staticFinal2);
        // Does trigger initialization:
        System.out.println(Initable2.staticNonFinal);
        Class initable3 = Class.forName("Initable3");
        System.out.println("After creating Initable3 ref");
        System.out.println(Initable3.staticNonFinal);
      }
    } /* Output:
    After creating Initable ref
    47
    Initializing Initable
    258
    Initializing Initable2
    147
    Initializing Initable3
    After creating Initable3 ref
    74
    *///:~
    
    

    如果一个static域不是final的,那么在对它访问时,总是要求在它被读取前,先进行链接(分配存储空间)和初始化(初始化存储空间)

    14.2.2 泛化的Class引用

    Class引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表达的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。
    通过允许你对Class引用所指向的Class对象的类型进行限定,将它的类型变得更具体一些。

    public class GenericClassReferences
    {
        public static void main(String[] args)
        {
            Class intClass = int.class;
            Class<Integer> genericIntClass = int.class;//只能指向具体的类型
            genericIntClass = Integer.class; // Same thing
            intClass = double.class;
            //genericIntClass = double.class; // Illegal
        }
    } ///:~
    

    通配符:? 表示任何事物。

    public class WildcardClassReferences {
      public static void main(String[] args) {
        Class<?> intClass = int.class;
        intClass = double.class;
      }
    } ///:~
    

    Class优于平凡得class,即便他们是等价得。Class的好处表达你知道你选择的是非具体的版本。
    将通配符和extends关键字结合使用:

    public class BoundedClassReferences {
      public static void main(String[] args) {
        Class<? extends Number> bounded = int.class;
        bounded = double.class;
        bounded = Number.class;
        // Or anything else derived from Number.
      }
    } ///:~
    

    下面的例子,它存储了一个引用,稍后又产生了一个List,填充这个List对象使用newInstance()方法:

    import java.util.*;
    
    class CountedInteger {
      private static long counter;
      private final long id = counter++;
      public String toString() { return Long.toString(id); }
    }
    
    public class FilledList<T> {
      private Class<T> type;
      public FilledList(Class<T> type) { this.type = type; }	
      public List<T> create(int nElements) {
        List<T> result = new ArrayList<T>();
        try {
          for(int i = 0; i < nElements; i++)
            result.add(type.newInstance());
        } catch(Exception e) {
          throw new RuntimeException(e);
        }
        return result;
      }
      public static void main(String[] args) {
        FilledList<CountedInteger> fl =
          new FilledList<CountedInteger>(CountedInteger.class);
        System.out.println(fl.create(15));
      }
    } /* Output:
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
    *///:~
    

    newInstance()将返回对象的确切类型:

    public class GenericToy
    {
        public static void main(String[] args) throws Exception
        {
            Class<B> b = B.class;
            System.out.println("a Name:" + b.getName());
            B b1 = b.newInstance();
            System.out.println(b);
            A a = (A)b.getSuperclass().newInstance();
            System.out.println("getSuperclass.Name:"+a);
            A a1 = b.newInstance();
            System.out.println(a1);
    
            Object o = b.getSuperclass();
            System.out.println(o);
        }
    }
    class A{
        static    {
            System.out.println("AAAA");
        }
        public String toString() {
            return "a";
        }
    }
    class B extends A{
        static    {
            System.out.println("BBBB");
        }
        public String toString()    {
            return "b";
        }
    }
    
    14.2.3 新的转型语法

    Class引用的转型语法cast()方法:接受参入参数对象,转换为引用类型

    class Building {}
    class House extends Building {}
    
    public class ClassCasts {
      public static void main(String[] args) {
        Building b = new House();
        Class<House> houseType = House.class;
        House h = houseType.cast(b);
        h = (House)b; // ... or just do this.
      }
    } ///:~
    

    Class.asSubclass():允许你将一个类对象转型为更加具体的类型。

    14.3 类型转换前先做检查

    我们已知的RTTI形式包括:

    1. 传统的类型转换
    2. 代表对象的类型的CLass对象。查询Class对象获取允许时所需要的信息。
    3. 关键字instanceof。它返回一个布尔值,告诉我们对象是不是某个特定类型的实例。

    instanceof只可将其与命名空间类型进行比较,而不能与Class对象作比较。

    14.4 注册工厂

    使用工厂方法设计模式,将对象的创建工作交给类自己去完成,工厂方法可以被多态的调用,从而为你创建恰当类型的对象。
    使用工厂创建对象:

    //: typeinfo/RegisteredFactories.java
    // Registering Class Factories in the base class.
    import factory.*;
    import java.util.*;
    
    class Part {
      public String toString() {
        return getClass().getSimpleName();
      }
      static List<Factory<? extends Part>> partFactories =
        new ArrayList<Factory<? extends Part>>();	
      static {
        // Collections.addAll() gives an "unchecked generic
        // array creation ... for varargs parameter" warning.
        partFactories.add(new FuelFilter.Factory());
        partFactories.add(new AirFilter.Factory());
        partFactories.add(new CabinAirFilter.Factory());
        partFactories.add(new OilFilter.Factory());
        partFactories.add(new FanBelt.Factory());
        partFactories.add(new PowerSteeringBelt.Factory());
        partFactories.add(new GeneratorBelt.Factory());
      }
      private static Random rand = new Random(47);
      public static Part createRandom() {
        int n = rand.nextInt(partFactories.size());
        return partFactories.get(n).create();
      }
    }	
    
    class Filter extends Part {}
    
    class FuelFilter extends Filter {
      // Create a Class Factory for each specific type:
      public static class Factory
      implements factory.Factory<FuelFilter> {
        public FuelFilter create() { return new FuelFilter(); }
      }
    }
    
    class AirFilter extends Filter {
      public static class Factory
      implements factory.Factory<AirFilter> {
        public AirFilter create() { return new AirFilter(); }
      }
    }	
    
    class CabinAirFilter extends Filter {
      public static class Factory
      implements factory.Factory<CabinAirFilter> {
        public CabinAirFilter create() {
          return new CabinAirFilter();
        }
      }
    }
    
    class OilFilter extends Filter {
      public static class Factory
      implements factory.Factory<OilFilter> {
        public OilFilter create() { return new OilFilter(); }
      }
    }	
    
    class Belt extends Part {}
    
    class FanBelt extends Belt {
      public static class Factory
      implements factory.Factory<FanBelt> {
        public FanBelt create() { return new FanBelt(); }
      }
    }
    
    class GeneratorBelt extends Belt {
      public static class Factory
      implements factory.Factory<GeneratorBelt> {
        public GeneratorBelt create() {
          return new GeneratorBelt();
        }
      }
    }	
    
    class PowerSteeringBelt extends Belt {
      public static class Factory
      implements factory.Factory<PowerSteeringBelt> {
        public PowerSteeringBelt create() {
          return new PowerSteeringBelt();
        }
      }
    }	
    
    public class RegisteredFactories {
      public static void main(String[] args) {
        for(int i = 0; i < 10; i++)
          System.out.println(Part.createRandom());
      }
    } /* Output:
    GeneratorBelt
    CabinAirFilter
    GeneratorBelt
    AirFilter
    PowerSteeringBelt
    CabinAirFilter
    FuelFilter
    PowerSteeringBelt
    PowerSteeringBelt
    FuelFilter
    *///:~
    

    14.5 instanceof与Class的等价性

    在查询类型信息时,以instanceof的形式与直接比较Class对象有一个很重要的差别,下面看看这些差别:

    //: typeinfo/FamilyVsExactType.java
    // The difference between instanceof and class
    
    class Base {}
    class Derived extends Base {}	
    
    public class FamilyVsExactType {
      static void test(Object x) {
        System.out.println("Testing x of type " + x.getClass());
        System.out.println("x instanceof Base " + (x instanceof Base));
        System.out.println("x instanceof Derived "+ (x instanceof Derived));
        System.out.println("Base.isInstance(x) "+ Base.class.isInstance(x));
        System.out.println("Derived.isInstance(x) " +
          Derived.class.isInstance(x));
        System.out.println("x.getClass() == Base.class " +
          (x.getClass() == Base.class));
        System.out.println("x.getClass() == Derived.class " +
          (x.getClass() == Derived.class));
        System.out.println("x.getClass().equals(Base.class)) "+
          (x.getClass().equals(Base.class)));
        System.out.println("x.getClass().equals(Derived.class)) " +
          (x.getClass().equals(Derived.class)));
      }
      public static void main(String[] args) {
        test(new Base());
        test(new Derived());
      }	
    } /* Output:
    Testing x of type class typeinfo.Base
    x instanceof Base true
    x instanceof Derived false
    Base.isInstance(x) true
    Derived.isInstance(x) false
    x.getClass() == Base.class true
    x.getClass() == Derived.class false
    x.getClass().equals(Base.class)) true
    x.getClass().equals(Derived.class)) false
    Testing x of type class typeinfo.Derived
    x instanceof Base true
    x instanceof Derived true
    Base.isInstance(x) true
    Derived.isInstance(x) true
    x.getClass() == Base.class false
    x.getClass() == Derived.class true
    x.getClass().equals(Base.class)) false
    x.getClass().equals(Derived.class)) true
    *///:~
    

    14.6 反射:运行时的类信息

    在编译时,编译器必须知道所要通过RTTI来处理的类。
    反射提供了一种机制——用来检查可用的方法,并返回方法名。
    人们想要在运行时获取类的信息的另一个动机,希望在跨网络的平台上创建和运行对象的能力。这被称为远程方法调用(RMI),它允许将一个Java程序分布到多台机器上。
    Class类与java.lang.reflect类库一起对反射概念进行了支持。这些类型的对象都是又JVM在运行时创建的,用以表示未知类里对应的成员。
    当通过反射与一个未知类型的对象打交道时,JVM只是简单的检查这个对象,看它属于哪个特定的类。在用它做其他事情之前必须先加载那个类的Class对象。因此,那个类的.Class文件对于JVM来说必须是可获取的:要么在本地机器上,要么可以通过网络获取。所以RTTI和反射之间真正的区别只在于du:对RTTI来说,编译器在编译时打开和检查.class文件,对于反射来说:.class在编译时是不可获取的,所有在运行时打开和检查.class文件。

    14.6.1 类方法提取器

    反射机制提供了一种方法,使我们能够编写可以自动展示完整接口的简单工具:

    
    

    14.7 动态代理

    代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替实际对象的对象。

    interface Interface {
      void doSomething();
      void somethingElse(String arg);
    }
    class RealObject implements Interface {
      public void doSomething() { System.out.println("doSomething"); }
      public void somethingElse(String arg) {
        System.out.println("somethingElse " + arg);
      }
    }	
    
    class SimpleProxy implements Interface {
      private Interface proxied;
      public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
      }
      public void doSomething() {
        System.out.println("SimpleProxy doSomething");
        proxied.doSomething();
      }
      public void somethingElse(String arg) {
        System.out.println("SimpleProxy somethingElse " + arg);
        proxied.somethingElse(arg);
      }
    }	
    
    class SimpleProxyDemo {
      public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
      }
      public static void main(String[] args) {
        consumer(new RealObject());
        consumer(new SimpleProxy(new RealObject()));
      }
    }
    

    Java的动态代理可以动态创建并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。

    import java.lang.reflect.*;
    
    class DynamicProxyHandler implements InvocationHandler {
      private Object proxied;
      public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
      }
      public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
        System.out.println("**** proxy: " + proxy.getClass() +
          ", method: " + method + ", args: " + args);
        if(args != null)
          for(Object arg : args)
            System.out.println("  " + arg);
        return method.invoke(proxied, args);
      }
    }	
    
    class SimpleDynamicProxy {
      public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
      }
      public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface)Proxy.newProxyInstance(
          Interface.class.getClassLoader(),
          new Class[]{ Interface.class },
          new DynamicProxyHandler(real));
        consumer(proxy);
      }
    } 
    

    动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个实际对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。

    14.8 空对象

    引入空对象的思想是很有用的,它可以接收传递给它的所代表的对象的消息,但是将返回表示为实际上并不存在的任何真实对象的值。通过这种方式,你可以假设所有的对象都是有效的,而不必浪费时间去检查null。
    即使空对象可以响应实际对象可以响应的所有消息,任需要测试是否为空:

    public interface Operation {
      String description();
      void command();
    } 
    
    class Person {
      public final String first;
      public final String last;
      public final String address;
      // etc.
      public Person(String first, String last, String address){
        this.first = first;
        this.last = last;
        this.address = address;
      }	
      public String toString() {
        return "Person: " + first + " " + last + " " + address;
      }
      public static class NullPerson
      extends Person implements Null {
        private NullPerson() { super("None", "None", "None"); }
        public String toString() { return "NullPerson"; }
      }
      public static final Person NULL = new NullPerson();
    } 
    
    class Position {
      private String title;
      private Person person;
      public Position(String jobTitle, Person employee) {
        title = jobTitle;
        person = employee;
        if(person == null)
          person = Person.NULL;
      }
      public Position(String jobTitle) {
        title = jobTitle;
        person = Person.NULL;
      }	
      public String getTitle() { return title; }
      public void setTitle(String newTitle) {
        title = newTitle;
      }
      public Person getPerson() { return person; }
      public void setPerson(Person newPerson) {
        person = newPerson;
        if(person == null)
          person = Person.NULL;
      }
      public String toString() {
        return "Position: " + title + " " + person;
      }
    } 
    
    //: typeinfo/Staff.java
    import java.util.*;
    
    public class Staff extends ArrayList<Position> {
      public void add(String title, Person person) {
        add(new Position(title, person));
      }
      public void add(String... titles) {
        for(String title : titles)
          add(new Position(title));
      }
      public Staff(String... titles) { add(titles); }
      public boolean positionAvailable(String title) {
        for(Position position : this)
          if(position.getTitle().equals(title) &&
             position.getPerson() == Person.NULL)
            return true;
        return false;
      }	
      public void fillPosition(String title, Person hire) {
        for(Position position : this)
          if(position.getTitle().equals(title) &&
             position.getPerson() == Person.NULL) {
            position.setPerson(hire);
            return;
          }
        throw new RuntimeException(
          "Position " + title + " not available");
      }	
      public static void main(String[] args) {
        Staff staff = new Staff("President", "CTO",
          "Marketing Manager", "Product Manager",
          "Project Lead", "Software Engineer",
          "Software Engineer", "Software Engineer",
          "Software Engineer", "Test Engineer",
          "Technical Writer");
        staff.fillPosition("President",
          new Person("Me", "Last", "The Top, Lonely At"));
        staff.fillPosition("Project Lead",
          new Person("Janet", "Planner", "The Burbs"));
        if(staff.positionAvailable("Software Engineer"))
          staff.fillPosition("Software Engineer",
            new Person("Bob", "Coder", "Bright Light City"));
        System.out.println(staff);
      }
    } /* Output:	
    [Position: President Person: Me Last The Top, Lonely At, Position: CTO NullPerson, Position: Marketing Manager NullPerson, Position: Product Manager NullPerson, Position: Project Lead Person: Janet Planner The Burbs, Position: Software Engineer Person: Bob Coder Bright Light City, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Test Engineer NullPerson, Position: Technical Writer NullPerson]
    *///:~
    

    使用命令模式:

    //: typeinfo/Robot.java
    import java.util.*;
    public interface Robot {
      String name();
      String model();
      List<Operation> operations();
      class Test {
        public 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());
            operation.command();
          }
        }
      }
    } ///:~
    
    //: typeinfo/SnowRemovalRobot.java
    import java.util.*;
    
    public class SnowRemovalRobot implements Robot {
      private String name;
      public SnowRemovalRobot(String name) {this.name = name;}
      public String name() { return name; }
      public String model() { return "SnowBot Series 11"; }
      public List<Operation> operations() {
        return Arrays.asList(
          new Operation() {
            public String description() {
              return name + " can shovel snow";
            }
            public void command() {
              System.out.println(name + " shoveling snow");
            }
          },	
          new Operation() {
            public String description() {
              return name + " can chip ice";
            }
            public void command() {
              System.out.println(name + " chipping ice");
            }
          },
          new Operation() {
            public String description() {
              return name + " can clear the roof";
            }
            public void command() {
              System.out.println(name + " clearing roof");
            }
          }
        );
      }	
      public static void main(String[] args) {
        Robot.Test.test(new SnowRemovalRobot("Slusher"));
      }
    } /* Output:
    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
    *///:~
    
    //: typeinfo/NullRobot.java
    // Using a dynamic proxy to create a Null Object.
    import java.lang.reflect.*;
    import java.util.*;
    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 {
        public String name() { return nullName; }
        public String model() { return nullName; }
        public List<Operation> operations() {
          return Collections.emptyList();
        }
      }	
      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) {
        Robot[] bots = {
          new SnowRemovalRobot("SnowBee"),
          newNullRobot(SnowRemovalRobot.class)
        };
        for(Robot bot : bots)
          Robot.Test.test(bot);
      }
    } /* Output:
    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
    *///:~
    

    14.8.1 模拟对象与桩

    空对象的逻辑变体是模拟对象和桩。模拟对象和桩都只是假扮可以传递实际信息的存活对象,而不是像空对象那样可以称为null的一种更加智能化的替代品。

    14.9 接口与类型信息

    interface关键字的一种重要目标就是允许程序员隔离构建,进而降低耦合性。通过类型信息,这种耦合性还是会传播出去——接口并非是对解耦的一种无懈可击的保障。

    package interfacea;
    
    public interface A {
      void f();
    } 
    
    import interfacea.*;
    
    class B implements A {
      public void f() {}
      public void g() {}
    }
    
    public class InterfaceViolation {
      public static void main(String[] args) {
        A a = new B();
        a.f();
        // a.g(); // Compile error
        System.out.println(a.getClass().getName());
        if(a instanceof B) {
          B b = (B)a;
          b.g();
        }
      }
    }
    

    最简单的方式是对实现使用包访问权限,这样在包外部的客户端就不能看见它了。

    package packageaccess;
    import interfacea.*;
    class C implements A {
      public void f() { System.out.println("public C.f()"); }
      public void g() { System.out.println("public C.g()"); }
      void u() { System.out.println("package C.u()"); }
      protected void v() { System.out.println("protected C.v()"); }
      private void w() { System.out.println("private C.w()"); }
    }
    
    public class HiddenC {
      public static A makeA() { return new C(); }
    }
    

    这里makeA返回C类型,但在包在并不能使用到C。但是反射却仍旧可以调用:

    import interfacea.*;
    import packageaccess.*;
    import java.lang.reflect.*;
    
    public class HiddenImplementation {
      public static void main(String[] args) throws Exception {
        A a = HiddenC.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        // Compile error: cannot find symbol 'C':
        /* if(a instanceof C) {
          C c = (C)a;
          c.g();
        } */
        // Oops! Reflection still allows us to call g():
        callHiddenMethod(a, "g");
        // And even methods that are less accessible!
        callHiddenMethod(a, "u");
        callHiddenMethod(a, "v");
        callHiddenMethod(a, "w");
      }
      static void callHiddenMethod(Object a, String methodName)
      throws Exception {
        Method g = a.getClass().getDeclaredMethod(methodName);
        g.setAccessible(true);
        g.invoke(a);
      }
    } /* Output:
    public C.f()
    typeinfo.packageaccess.C
    public C.g()
    package C.u()
    protected C.v()
    private C.w()
    *///:~
    

    即使是内部类,反思仍旧可以调用到:

    import interfacea.*;
    class InnerA {
      private static class C implements A {
        public void f() { System.out.println("public C.f()"); }
        public void g() { System.out.println("public C.g()"); }
        void u() { System.out.println("package C.u()"); }
        protected void v() { System.out.println("protected C.v()"); }
        private void w() { System.out.println("private C.w()"); }
      }
      public static A makeA() { return new C(); }
    }	
    
    public class InnerImplementation {
      public static void main(String[] args) throws Exception {
        A a = InnerA.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        // Reflection still gets into the private class:
        HiddenImplementation.callHiddenMethod(a, "g");
        HiddenImplementation.callHiddenMethod(a, "u");
        HiddenImplementation.callHiddenMethod(a, "v");
        HiddenImplementation.callHiddenMethod(a, "w");
      }
    }
    

    匿名类也一样:

    import interfacea.*;
    class AnonymousA {
      public static A makeA() {
        return new A() {
          public void f() { System.out.println("public C.f()"); }
          public void g() { System.out.println("public C.g()"); }
          void u() { System.out.println("package C.u()"); }
          protected void v() { System.out.println("protected C.v()"); }
          private void w() { System.out.println("private C.w()"); }
        };
      }
    }
    public class AnonymousImplementation {
      public static void main(String[] args) throws Exception {
        A a = AnonymousA.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        // Reflection still gets into the anonymous class:
        HiddenImplementation.callHiddenMethod(a, "g");
        HiddenImplementation.callHiddenMethod(a, "u");
        HiddenImplementation.callHiddenMethod(a, "v");
        HiddenImplementation.callHiddenMethod(a, "w");
      }
    } /* Output:
    public C.f()
    AnonymousA$1
    public C.g()
    package C.u()
    protected C.v()
    private C.w()
    *///:~
    
    
  • 相关阅读:
    geodatabase在sql server和Oracle上的应用
    python:AttributeError: module 'xxxx' has no attribute 'xxxxx'
    sql server复制一张表到另一张表
    pyodbc:Python使用pyodbc访问数据库操作方法
    python如何生成token
    WebGIS应用案例
    SAP UI5 里如何让每次视图显示时都执行某方法
    使用开源微前端框架 Luigi 创建一个基于微前端架构的工程
    关于 SAP 产品 UI 的搜索引擎优化 SEO
    Debug a Server–Side Rendered SAP Spartacus Storefront Using Chrome Dev Tools
  • 原文地址:https://www.cnblogs.com/Tan-sir/p/11280948.html
Copyright © 2011-2022 走看看