|     java inner class closure   |    

引子

首先,内部类和外部类能无障碍互相访问是一个事实,

外部类能访问内部类的所有成员,包括private成员。 内部类也能访问外部类的所有成员,包括private成员。

问题:外部类的派生类无法访问内部类??

Thinking in Java - Chapter 10 Inner Class - Exercise 6的问题如下:

Exercise 6: (2) Create an interface with at least one method, in its own package. Create a class in a separate package. Add a protected inner class that implements the interface. In a third package, inherit from your class and, inside a method, return an object of the protected inner class, upcasting to the interface during the return.

今天犯了一个有意思的错误。

packA里有个Outer类,它包含一个内部类InnerOuter类的inner()工厂方法,创建并返回Inner类实例的引用。一切运行正常。

package com.ciaoshen.packA;

public class Outer {
        protected class Inner {
            public void foo() { System.out.println("Hello Ronald!"); }
        }
        protected Inner inner() {
            return new Inner();
        }
        public static void main(String[] args) {
            new Outer().inner().foo(); // Output: Hello Ronald!
        }
}

现在换另一个包packBDerivedOuter继承自Outer. DerivedOuter调用继承自Outerinner()方法。还是一切正常。

package com.ciaoshen.packB;

class DerivedOuter extends Outer {
    public static void main(String[] args) {
        new DerivedOuter().inner().foo(); // Output: Hello Ronald!
    }
}

但一旦重写 DerivedOuter里的inner()方法,虽然只是改了一个权限,方法内容照旧。但出现系统报错:ERROR: Outer.Inner() has protected access in Outer.Inner.

package com.ciaoshen.packB;

class DerivedOuter extends Outer {
    @Override
    public Inner inner() { // this is not the inner() of Outer class. BOOM!
        return new Inner();
    }
    public static void main(String[] args) {
        new DerivedOuter().inner().foo(); // ERROR: Outer.Inner() has protected access in Outer.Inner
    }
}

DerivedOuter不是继承了Outer了吗? 为什么不能调用Inner的构造函数? 况且原先没有重写的时候,一切正常。

为题就出在“重写”上! 根据Java的继承规则,派生类不是彻彻底底一个新的类。而是先初始化基类。也就是说 派生类里包含了一个基类。而且派生类和基类的“成员字段”分得清清楚楚,甚至当重名是,同时保留两份同名字段,一份用super引用,一份用this引用。只有“方法”是共用的。但 方法一旦被“重写”,就不是原来基类的方法。

也就是说,不是像一般想象的那样,DerivedOuter继承自Outer就包含了Inner,就可以对Inner做任何操作。 实际情况是,DerivedOuter继承了Outer的所有方法,操作Inner也是通过调用Outer原生的inner()方法完成。因为Inner是在Outer里声明的,只有原生的Outer方法对Inner拥有一切权限。后面在DerivedOuter里加入的新方法,包括重写Outer类里的inner(),统统无效,无法访问Innerprotected成员。

总结

这就是为什么Thinking in Java书里说:内部类可以很方便的隐藏实现细节

只要内部类声明为private,除了外部类,没有人能访问它。

如果内部类声明为protected,只有外部类及其子类,还有同一个包内的类能够访问它。而且也只是能访问它的实例,不能访问它的protected或者package access或者private的成员。

如果返回引用前,再向上转型成某些公开接口的话,用户甚至都无法向下转型成具体类型。