内部类的定义java
将一个类Inner的定义放在另外一个类Outer的定义内部. 则Inner的具体类型为Outer.Inner闭包
若是要引用Inner类型, 咱们须要Outer.Inner, 即在类层次上, Inner是寄生于Outer的, 任何关于Inner的操做(如构造, 调用其方法)都须要经过Outer的实例对象生成一个Inner的对象(这样才能跟Outer.Inner类型关联起来)来进行操做.ide
public class Outer { public String s; Outer(String s) { this.s = s; } class Inner { public void show() { System.out.println(s); System.out.println("Inner show"); } } public Inner inner() { return new Inner(); } public static void main(String[] args) { Outer o = new Outer("???"); Outer.Inner i = o.inner(); i.show(); System.out.println(o.s); // ERROR // Outer.Inner i1 = new Outer.Inner(); Outer.Inner i1 = o.new Inner(); i1.show(); } }
就像是任何类方法内部可使用this来调用此类的全部成员同样. 内部类也一样使用隐式的"this指针"来访问外部类的全部成员,而不须要任何特殊条件(相似动态语言如Python,JavaScript的闭包原理).测试
.this与.new优化
若是要生成对外部类的引用, 须要.thisthis
public class DotThis { void f() { System.out.println("DotThis.f()"); } public class Inner { public DotThis outer() { return DotThis.this; } } public Inner inner() { return new Inner(); } public static void main(String[] args) { DotThis dt = new DotThis(); DotThis.Inner dti = dt.inner(); dti.outer().f(); } }
这里不能使用return DotThis. 是由于DotThis是一个类, 而DotThis.this是一个引用对象,指的是当前内部类所引用的外部对象.spa
若是要建立内部类的对象, 则须要.new. 在拥有外部类对象以前不可能建立内部类对象的,这是由于内部类对象会暗暗的链接到建立它的外部类对象上.设计
public class DotNew { public class Inner { public void show() { System.out.println("Inner show"); } } public static void main(String[] args) { DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); dni.show(); } }
内部类与向上转型指针
内部类的一个用途在于: 实现一个接口. 这样内部类可向上转型为一个接口的对象:code
interface A { String toString(); } public class C { private class B implements A { public String toString() { return getClass().getName(); } } public static void main(String[] args) { C c = new C(); C.B b = c.new B(); System.out.println(b); } }
这种设计符合组合思想,接口的实现类成为具体类的内部对象,从而很好的隐藏其实现细节.
定义在方法中的类/定义在做用域中的类
定义在方法或做用域中的类, 主要为了解决如下状况: 方法/做用域的逻辑过于复杂, 咱们须要建立一个类来辅助解决, 但又不但愿这个类是公共可用的.
如下笔记主要基于下例几点:
1. 一个定义在方法中的类.
2. 一个定义在做用域内的类,此做用域在方法的内部.
3. 一个实现了接口的匿名类.
4. 一个匿名类,它扩展了有非默认构造器的类.
5. 一个匿名类,它执行字段初始化.
6. 一个匿名类,它经过实例初始化实现构造.
定义在方法中的类
interface A { String toString(); } public class C { public A show() { class B implements A { public String toString() { return getClass().getName(); } } return new B(); } public static void main(String[] args) { C c = new C(); // C$1B System.out.println(c.show()); } }
定义在做用域内中的内部类
interface A { String toString(); } public class C { public String show(boolean b) { if (b) { class B implements A { public String toString() { return getClass().getName(); } } A a = new B(); return a.toString(); } else { return "error"; } } public static void main(String[] args) { C c = new C(); System.out.println(c.show(true)); System.out.println(c.show(false)); } }
匿名内部类
interface Contents { int value(); } public class Parcel7 { public Contents contents() { return new Contents() { private int i = 11; public int value() { return i; } }; } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } }
对于匿名内部类的语法解析以下: new表明新建一个对象, 调用的是Contents()构造器, 其后增长的是类的实际定义.
备注: 这跟动态语言, 如Python/JavaScript的闭包同样.
上述的匿名内部类语法就是下述形式的简化形式:
interface Contents { int value(); } public class Parcel7 { class MyCOntents implements Contents { private int i = 11; @Override public int value() { return i; } } public Contents contents() { return new MyCOntents(); } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); System.out.println(c.value()); } }
若是匿名内部类使用了外部的参数, 那么其参数必须命名为final禁止被修改. 而在匿名类中一样可使用实例初始化(即下例代码中大括号{}部分)来达到相似构造器的效果:
interface Contents { int value(); } public class Parcel7 { public Contents contents(final int value) { return new Contents() { private int i; { i = value; } public int value() { return i; } }; } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(42); System.out.println(c.value()); } }
使用匿名内部类能够优化其工厂方法:
interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() {} public void method1() { System.out.println("Implementation1 method1."); } public void method2() { System.out.println("Implementation1 method2."); } public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { return new Implementation1(); } }; } class Implementation2 implements Service { private Implementation2() {} public void method1() { System.out.println("Implementation2 method1."); } public void method2() { System.out.println("Implementation2 method2."); } public static ServiceFactory factory = new ServiceFactory() { @Override public Service getService() { return new Implementation2(); } }; } public class Factories { public static void serviceConsume(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsume(Implementation1.factory); serviceConsume(Implementation2.factory); } }
嵌套类
使用static声明的内部类为嵌套类, 及它跟外围类的实例对象并无任何的关联.
当内部类为static时,意味着:
1. 要建立嵌套类的对象, 并不须要其外围类的对象.
2. 不能从嵌套类的对象中访问非静态的外围类对象.
interface A { String toString(); } public class C { public static class B implements A { public String toString() { return getClass().getName(); } } public static void main(String[] args) { A a = new C.B(); System.out.println(a); } }
假设咱们须要在接口中编写通用的公用代码, 用于不一样接口实现的类所公用, 那么在接口中内嵌类是很是好的方法:
interface A{ void howdy(); class Test implements A{ public void howdy() { System.out.println("Howdy"); } public static void main(String[] args) { new Test().howdy(); } } } public class ClassInInterface implements A{ public void howdy() {} public static void main(String[] args) { A.Test t = new A.Test(); t.howdy(); } }
咱们一般在类中编写main来测试这个类. 若是嫌麻烦咱们可使用嵌套类来实现测试代码:
public class TestBed { public void f() { System.out.println("f()"); } public static class Tester { public static void main(String[] args) { TestBed t = new TestBed(); t.f(); } } }
执行: java TestBed$Tester便可测试.
主要缘由在于: 每一个内部类都能独立的继承自一个(接口的)实现, 因此不管外围类是否已经继承了某个(接口的)实现, 对于内部类都没有影响.
针对多重继承, 接口只解决了部分问题, 内部类使之获得完善.
考虑如下场景: 即必须在一个类中以某种方式实现两个接口. 这时候, 咱们有两个选择: 要么使用单一类(所有implements两个接口), 要么使用内部类:
interface A { void show(); } interface B { void func(); } class X implements A, B { public void show() { System.out.println("X show"); } public void func() { System.out.println("X func"); } } class Y implements A { public void show() { System.out.println("Y show"); } B makeB() { // 返回一个匿名类 return new B() { public void func() { System.out.println("Y func"); } }; } } public class MultiInterfaces { static void takeA(A a) { a.show(); } static void takeB(B b) { b.func(); } public static void main(String[] args) { X x = new X(); Y y = new Y(); takeA(x); takeA(y); takeB(x); takeB(y.makeB()); } }
但若是拥有抽象类或具体类,而不是接口, 则只能使用内部类才能实现多重继承.
interface A { void show(); } abstract class B { abstract void func(); } class Y implements A { public void show() { System.out.println("Y show"); } B makeB() { // 返回一个匿名类 return new B() { public void func() { System.out.println("Y func"); } }; } } public class MultiInterfaces { static void takeA(A a) { a.show(); } static void takeB(B b) { b.func(); } public static void main(String[] args) { Y y = new Y(); takeA(y); takeB(y.makeB()); } }
1. 内部类能够有多个实例, 每一个实例都有本身的状态信息, 而且与其外围类对象的信息相互独立.
2. 在单个外围类中, 可让多个内部类以不一样的方式实现同一个接口, 或继承同一个类.
3. 建立内部类对象并不依赖于外围类对象的建立.
4. 内部类是独立的实体.
闭包与回调
闭包是一个可调用的对象,它记录了一些信息,这些信息来自于建立它的做用域. 经过此定义,能够看出内部类是面向对象的闭包, 由于它不只包含外围类对象(建立内部类的做用域)的信息, 还自动拥有一个指向此外围类对象的引用, 在此做用域内, 内部类有权操做全部的成员, 包括private成员.
内部类的继承
在继承内部类的时候, 因为内部类关联一个外部类的实例, 因此大概格式以下:
class WithInner { class Inner{} } public class InheritInner extends WithInner.Inner { InheritInner(WithInner wi) { wi.super(); } public static void main(String[] args) { WithInner wi = new WithInner(); InheritInner ii = new InheritInner(wi); } }
内部类的覆盖
两个内部类是独立的两个实体,各自在本身的命名空间, 它们须要具体的外部类实例进行引用.
class Egg { private Yolk y; protected class Yolk { public Yolk() { System.out.println("Egg.Yolk()"); } } public Egg() { System.out.println("New Egg()"); y = new Yolk(); } } public class BigEgg extends Egg { public class Yolk { public Yolk() { System.out.println("BigEgg Yolk()"); } } public static void main(String[] args) { //New Egg() //Egg.Yolk() new BigEgg(); } }
局部内部类
咱们能够在一个方法体的里面建立一个内部类,局部内部类不能有访问说明符,由于它不是外围类的一部分,可是它能够访问当前代码块内的常量,以及此外围类的全部成员.
下例对局部内部类与匿名内部类的建立进行了比较:
interface Counter { int next(); } public class LocalInnerClass { private int count = 0; Counter getCounter(final String name) { class LocalCounter implements Counter { public LocalCounter() { System.out.println("LocalCounter()"); } public int next() { System.out.print(name); return count++; } } return new LocalCounter(); } Counter getCounter2(final String name) { return new Counter() { { System.out.println("Counter"); } @Override public int next() { System.out.print(name); return count++; } }; } public static void main(String[] args) { LocalInnerClass lic = new LocalInnerClass(); Counter c1 = lic.getCounter("Local inner "); Counter 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()); } }
输出:
LocalCounter() Counter Local inner 0 Local inner 1 Local inner 2 Local inner 3 Local inner 4 Anonymous inner5 Anonymous inner6 Anonymous inner7 Anonymous inner8 Anonymous inner9
使用局部类而不是匿名内部类的惟一理由是: 咱们须要一个已命名的构造器,或者须要重载构造器, 而匿名内部类只能用于实例初始化.