publicclassParcel2{ classContents{ privateint i = 11;
publicintvalue(){ return i; } }
classDestination{ private String label;
Destination(String whereTo) { label = whereTo; }
String readLabel(){ return label; } }
public Destination to(String s){ returnnew Destination(s); }
public Contents contents(){ returnnew Contents(); }
publicvoidship(String dest){ Contents c = contents(); Destination d = to(dest); System.out.println(d.readLabel()); }
publicstaticvoidmain(String[] args){ Parcel2 p = new Parcel2(); p.ship("Tasmania"); Parcel2 q = new Parcel2(); // Defining references to inner classes: Parcel2.Contents c = q.contents(); Parcel2.Destination d = q.to("Borneo"); } }
// output: Tasmania
官方给的例子有点不太好记忆, 本人更倾向于简单的直接 Outer/Inner 这中名字来命名 class
1 2 3 4 5 6 7 8 9 10 11
publicclassOuter{ classInner{}
public Inner getInner(){ returnnew Inner(); }
publicstaticvoidmain(String[] args){ Outer.Inner inner = new Outer().getInner(); } }
The link to the outer class
内部类最显著的特点: Inner class 创建的时候会持有一个外部类的引用, 概念上类似指针, 这使得他能没有限制的访问外部类成员变量和方法.
public Destination destination(String s){ returnnew PDestination(s); }
public Contents contents(){ returnnew PContents(); } }
Exc8: 确认下外部类是否能访问内部类变量? Determine whether an outer class has access to the private elements of its inner class. 看调用方式, 如果是外部类方法直接调用内部类成员变量, 不能, 外部类实例化后, 内部类可能压根就没有实例化, 访问个毛线 如果是实例化了, 就可以调用, 习题答案如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classOuter8{ classInner{ privateint ii1 = 1; privateint ii2 = 2; privatevoidshowIi2(){ System.out.println(ii2); } privatevoidhi(){ System.out.println("Inner hi"); } } // Need to create objects to access private elements of Inner: int oi = new Inner().ii1; voidshowOi(){ System.out.println(oi); } voidshowIi2(){ new Inner().showIi2(); } voidouterHi(){ new Inner().hi(); } publicstaticvoidmain(String[] args){ Outer8 out = new Outer8(); out.showOi(); out.showIi2(); out.outerHi(); } }
Inner classes in methods and scopes
前面那些例子都很直白易懂, 但是 Inner class 还有一些变种, 格式很放飞自我, 该变种适用如下情况
你只是想要实现某个接口, 并返回这个接口引用
你在解决某个复杂问题时, 临时需要创建一个 class 以解决问题, 但是不想暴露它的实现
下面我们会将前面的 Parcel 例子转化为以下几种方式:
1.A class defined within a method - 在方法体内定义类 2.A class defined within a scope inside a method - 在方法的某个更小的 scope 中声明类, 比如方法的 if 条件语句中 3.An anonymous class implementing an interface - 匿名内部类实现接口 4.An anonymous class extending a class that has a non-default constructor - 匿名内部类继承抽象类 + 自定义构造函数 5.An anonymous class that performs field initialization - 匿名内部类 + field 初始化 6.An anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors) - 匿名内部类 + 构造代码块
对应 item1: A class defined within a method, 我们将 class 创建在方法体内部, 这种做法也叫 本地内部类(local inner class):
publicclassParcel6{ privatevoidinternalTracking(boolean b){ if (b) { classTrackingSlip{ private String id;
TrackingSlip(String s) { id = s; }
String getSlip(){ return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } // Can’t use it here! Out of scope: // ! TrackingSlip ts = new TrackingSlip("x"); }
publicvoidtrack(){ internalTracking(true); }
publicstaticvoidmain(String[] args){ Parcel6 p = new Parcel6(); p.track(); } }
TrackingSlip 嵌在 if 语句中, 只在 if 里生效, 出了这个范围就失效了, 除此之外和其他内部类没什么区别.
Anonymous inner classes
对应 item3: An anonymous class implementing an interface 定义匿名内部类 和 item4: An anonymous class extending a class that has a non-default constructor 使用默认构造函数
publicclassParcel7b{ classMyContentsimplementsContents{ privateint i = 11;
publicintvalue(){ return i; } }
public Contents contents(){ returnnew MyContents(); }
publicstaticvoidmain(String[] args){ Parcel7b p = new Parcel7b(); Contents c = p.contents(); } }
看文章顺序这个应该是对应 item4: An anonymous class extending a class that has a non-default constructor 的但是总感觉他这种说法不贴切, 可能是我笔记有问题, 按理说, 下面的 instance initialization 更贴切才对.
publicstaticvoidmain(String[] args){ Parcel8 p = new Parcel8(); Wrapping w = p.wrapping(10); System.out.println(wrapping.value()); } }
publicclassWrapping{ privateint i;
publicWrapping(int x){ i = x; }
publicintvalue(){ return i; } }
// output: 470
对应 item5: An anonymous class that performs field initialization 你可以在内部类中定义, 使用 field, field 如果是作为参数传入, 必须是 final 类型的:
再看一遍才发现, 他的特殊之处是内部类有一个 field 声明, 对应的值是直接从方法参数里面拿的!!这种用法以前没注意到过 (; ̄ェ ̄)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
publicclassParcel9{ // Argument must be final to use inside // anonymous inner class: public Destination destination(final String dest){ returnnew Destination() { private String label = dest;
public String readLabel(){ return label; } }; }
publicstaticvoidmain(String[] args){ Parcel9 p = new Parcel9(); Destination d = p.destination("Tasmania"); } }
If you’re defining an anonymous inner class and want to use an object that’s defined outside the anonymous inner class, the compiler requires that the argument reference be final, as you see in the argument to destination(). If you forget, you’ll get a compile-time error message.
Remember the advice given at the end of the last chapter: Prefer classes to interfaces. If your design demands an interface, you’ll know it. Otherwise, don’t put it in until you are forced to.
一般来说, 在 interface 里放 class 是不允许的, 但是 nested class 是个例外. 任何放到 interface 里的 code 都会有 public 和 static 的属性, 所以下面代码中声明的 class class Test implements ClassInInterface 其实就是一个静态内部类. You can even implement the surrounding interface in the inner class, like this:
In a single outer class you can have several inner classes, each of which implements the same interface or inherits from the same class in a different way. An example of this will be shown shortly.
The point of creation of the inner-class object is not tied to the creation of the outer-class object.
There is no potentially confusing “is-a” relationship with the inner class; it’s a separate entity.
publicclassController{ // A class from java.util to hold Event objects: private List<Event> eventList = new ArrayList<Event>();
publicvoidaddEvent(Event c){ eventList.add(c); }
publicvoidrun(){ while (eventList.size() > 0) // Make a copy so you’re not modifying the list // while you’re selecting the elements in it: for (Event e : new ArrayList<Event>(eventList)) if (e.ready()) { System.out.println(e); e.action(); eventList.remove(e); } } }
publicvoidaction(){ // Put hardware control code here. thermostat = "Day"; }
public String toString(){ return"Thermostat on day setting"; } }
// An example of an action() that inserts a // new one of itself into the event list: publicclassBellextendsEvent{ publicBell(long delayTime){ super(delayTime); }
publicclassGreenhouseController{ publicstaticvoidmain(String[] args){ GreenhouseControls gc = new GreenhouseControls(); // Instead of hard-wiring, you could parse // configuration information from a text file here: gc.addEvent(gc.new Bell(900)); Event[] eventList = { gc.new ThermostatNight(0), gc.new LightOn(200), gc.new LightOff(400), gc.new WaterOn(600), gc.new WaterOff(800), gc.new ThermostatDay(1400) }; gc.addEvent(gc.new Restart(2000, eventList)); gc.addEvent(new GreenhouseControls.Terminate(new Integer(5000))); gc.run(); } } // output: // Bing! // Thermostat on night setting // Light is on // Light is off // Greenhouse water is on // Greenhouse water is off // Thermostat on day setting // Restarting system // Terminating
This example shows that there isn’t any extra inner-class magic going on when you inherit from the outer class. The two inner classes are completely separate entities, each in its own namespace. However, it’s still possible to explicitly inherit from the inner class:
Counter getCounter(final String name){ // A local inner class: classLocalCounterimplementsCounter{ publicLocalCounter(){ // Local inner class can have a constructor System.out.println("LocalCounter()"); }
publicintnext(){ System.out.print(name); // Access local final return count++; } } returnnew LocalCounter(); }
// The same thing with an anonymous inner class: Counter getCounter2(final String name){ returnnew Counter() { // Anonymous inner class cannot have a named // constructor, only an instance initializer: { System.out.println("Counter()"); }
publicintnext(){ System.out.print(name); // Access local final return count++; } }; }
publicstaticvoidmain(String[] args){ LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner "); for (int i = 0; i < 5; i++) System.out.println(c1.next()); for (int i = 0; i < 5; i++) System.out.println(c2.next()); } }
// output // LocalCounter() // Counter() // Local inner 0 // Local inner 1 // Local inner 2 // Local inner 3 // Local inner 4 // Anonymous inner 5 // Anonymous inner 6 // Anonymous inner 7 // Anonymous inner 8 // Anonymous inner 9
上面的例子中, Counter 接口会依次返回 count 值. local inner class 和 匿名内部类都实现了这个接口. 两个内部类逻辑和功能也都一样, 唯一区别是, 匿名内部类他是没有构造函数的, 需要用代码块代替.
如果你需要创建多个实例的话, 你也要使用 local inner class, 你用 anonymous 是建不出来多个实例的.