可以将一个类的定义放在另一个类的定义内部,这就是内部类
创建内部类
内部类看起来就像是一种代码隐藏机制,将类放在其他类的内部,内部类和组合是不同的概念
在外部类的方法里面使用内部类的时候与使用普通类没有什么区别
public class Parcel1 {class Contents { //把类的定义放置在外围类的里面private int i = 11;public int value(){return i;}}class Destination{private String label;Destination(String whereTo){this.label = whereTo;}String readLabel(){return label;}}public void ship (String dest){ //ship方法里面使用内部类,和使用普通类没什么区别Contents c = new Contents();Destination d = new Destination(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel1 p1 = new Parcel1();p1.ship("Hello world");}}
public class Parcel2 {class Contents{private int i = 10;private int value(){return i;}}class Destination{private String label;Destination (String whereTo){this.label = whereTo;}String readLabel(){return label;}}public Destination to( String s){return new Destination(s);}public Contents contents(){return new Contents();}public void ship(String dest){Contents c = new Contents();Destination d = to(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel2 p2 = new Parcel2();p2.ship("Hello World");Parcel2 p3 = new Parcel2();//如何操作外部类去链接内部类获取内部类对象Parcel2.Contents c = p3.contents();Parcel2.Destination d = p3.to("haha"); 钩子System.out.println(d.readLabel());}}
链接到外部类
10.2重点练习
interface Selector{boolean end();Object current();void next();}public class Sequence {private Object[] items;private int next = 0;public Sequence (int size){items = new Object[size];}public void add (Object x){if (next < items.length){items[next ++] = x;}}private class SequenceSelector implements Selector{private int i = 0;@Overridepublic boolean end() {return i == items.length;}@Overridepublic Object current() {return items[i];}@Overridepublic void next() {if (i < items.length){i++;}}}public Selector selector(){return new SequenceSelector();}public static void main(String[] args) {Sequence sequence = new Sequence(7);for (int i = 0 ; i < 7;i++){sequence.add(Integer.toString(i));}Selector selector = sequence.selector();while (!selector.end()){System.out.println(selector.current() + "");selector.next();}}}
.this 和 .new
如果生成对外部对象的引用的时候,可以使用外部类的名字后面加上.this,这样产生的应用会自动的具有正确的类型
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();//使用内部类的方法来调用了外部类的方法}}
.new 使用外部类实例加上.new的形式对内部类进行实例化
public class Parcel3 {class Contents{private int i = 11;public int value(){return i;}}class Destination{private String label;Destination(String whereTo){this.label = whereTo;System.out.println("wuhu");}String readLabel(){return label;}}public static void main(String[] args) {Parcel3 p3 = new Parcel3();//创建外围类的实例Parcel3.Contents c = p3.new Contents();//利用外围类加上.new的方式创建实例Parcel3.Destination d = p3 .new Destination("Hello");}}
内部类和向上转型
当内部类向上转型为基类或者接口的时候,这是因为这个内部类(某个接口的实现)能够完全不可见,而且不能够使用,得到的只是指向基类或者接口的引用,可以隐藏细节
当取得了一个指向基类或接口的引用时,甚至无法找到它的确切类型
class Parcel4{private class PContents implements Contents{ //私有的。除了Parcel4没有人可以访问private int i= 0;@Overridepublic int value() {return i;}}protected class PDestination implements Destination{ //除了Parcel4和他的子类。没人可以访问private String Label;private PDestination(String whereTo){this.Label = whereTo;System.out.println(Label);}@Overridepublic String readLabel() {return Label;}}public Contents contents(){return new PContents();//通过方法来返回一个接口的实例}public Destination destination(String s){return new PDestination(s);}}public class TestParcel {public static void main(String[] args) {Parcel4 parcel4 = new Parcel4();Contents c = parcel4.contents();Destination d = parcel4.destination("hah");}}
在方法和作用域中的内部类
在方法中或者在任意的作用域中定义内部类
(1)实现某个类型的接口,可以创建并且返回对它的引用
局部内部类:在方法的作用域中创建一个类
public class Parcel5 {public Contents contents() {class PContents implements Contents {//在方法内部的类,局部内部类private int i = 11;@Overridepublic int value() {return i;}}return new PContents();//返回接口的实现类}public Destination destination(String s) {class PDestination implements Destination {private String label;PDestination(String s1) {label = s1;}@Overridepublic String readLabel() {return label;}}return new PDestination(s);}public static void main(String[] args) {final Parcel5 parcel5 = new Parcel5();Contents contents = parcel5.contents();Destination destination = parcel5.destination("hehe");System.out.println(contents.value());System.out.println(destination.readLabel());}}
匿名内部类
匿名内部类实际上值得时父类引用指向子类的实现
创建一个继承来自一个接口的匿名类对象,通过new表达式返回的引用被自动转型为对接口的引用
public class Parcel7 {public Contents contents (){return new Contents() { //匿名类,单独拿出就是一个类private int i = 0;@Overridepublic int value() {return i;}}; //匿名内部类最后加分号}public static void main(String[] args) {Parcel7 parcel7 = new Parcel7();Contents c = parcel7.contents();}}
在匿名内部类中定义字段的时候还可以对其进行初始化
public class Parcel9 {public Destination destination(final String desk){return new Destination() {private String Label = desk; //对Label进行初始化@Overridepublic String readLabel() {return Label;}};}public static void main(String[] args) {Parcel9 parcel9 = new Parcel9();Destination destination = parcel9.destination("wuhu");}}
匿名内部类没有名字,如何匿名内部类中做一些类似构造器的行为?利用实例初始化
abstract class Base{public Base(int i){System.out.println("Base Constructor.i = " + i);}public abstract void f();}public class AnonymousConstructor {public Base getBase(int i){return new Base(i) {{System.out.println("实例初始化");//使用实例初始化的方式做出类似构造器的行为}@Overridepublic void f() {System.out.println("匿名类中的f()");}};}public static void main(String[] args) {AnonymousConstructor anonymousConstructor = new AnonymousConstructor();Base base = anonymousConstructor.getBase(10);base.f();}}
public class Parcel10 {public Destination destination(final String desk, final float price){return new Destination() {private int cost;{cost = Math.round(price);if (cost > 500){System.out.println("超出预算啦");}}private final String Label = desk; //在定义字段时对其进行初始化@Overridepublic String readLabel() {return Label;}};}public static void main(String[] args) {Parcel10 parcel10 = new Parcel10();Destination destination = parcel10.destination("玩具",501.001f);}}
匿名内部类和继承相比有些受限,匿名内部类可以扩展类,也可以实现接口,但是不能二者兼备,而且如果实现接口也只能实现一个接口
匿名内部类的工厂方法
interface Service{void method1();void method2();}interface ServiceFactory{Service getService();}class Imp1 implements Service{@Overridepublic void method1() {System.out.println("Imp1.method1()");}@Overridepublic void method2() {System.out.println("Imp1.method2()");}public static ServiceFactory factory(){return new ServiceFactory() {@Overridepublic Service getService() {return new Imp1();}};}}public class Factories {public static void serviceCun(ServiceFactory serviceFactory){Service s = serviceFactory.getService();s.method1();s.method2();}public static void main(String[] args) {serviceCun(Imp1.factory());}}
嵌套类
如果不需要内部类对象和外部类对象之间有联系,那么可以将内部类声明称为static,
普通内部类对象隐式的保存一个引用,这个引用指向了创建它的外部类对象,但是如果内部类是static的话就不是这样的了
如果内部类是static的话:如果要创建嵌套类的对象,就不需要外部类的对象了
不能从嵌套类的对象中访问不是静态的外部类对象
普通内部类中的字段和方法只能放在类的外部层次中,普通内部类中不能有static字段和数据也不能有嵌套类,但是嵌套类中可以有这些东西
public class Parcel11 {private static class ParcelContents implements Contents{private int i = 0;@Overridepublic int value() {return i;}}protected static class ParcelDestination implements Destination{private String Label;ParcelDestination(String whereTo){this.Label = whereTo;}@Overridepublic String readLabel() {return Label;}}public static void f(){}static int x = 10;static class AnotherLevel{public static void f(){}static int x = 20;}public static Destination destination(String s){return new ParcelDestination(s);}public static Contents contents(){return new ParcelContents();}public static void main(String[] args) {Contents c = contents(); //如果内部类是static的话:如果要创建嵌套类的对象就不需要外部类的对象了Destination d = destination("hah");AnotherLevel.f();f();System.out.println(x);System.out.println(AnotherLevel.x);}}
接口内部的类
嵌套类可以作为接口的一部分,并且放在接口中的任何类都自动的为public和static的
public interface ClassInInterface {void howdy();class Test implements ClassInInterface{@Overridepublic void howdy() {System.out.println("Howdy!");}public static void main(String[] args) {new Test().howdy();}}}
从多层嵌套类中访问外围类成员
一个内部类被嵌套多少层不重要,它都能透明的访问它所嵌入的外围类的所有成员
class MNA{private void f(){}class A{private void g(){}public class B{void h(){g();f();}}}}public class MultiNestingAccess {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A();MNA.A.B mnaab = mnaa.new B();mnaab.h();}
为什么需要内部类
首先每个内部类都能独立的继承来自一个接口的实现,所以无论外围类是否已经继承过了某个接口类的实现,对内部类来说都是没有影响的
内部类有效的继承了“多重继承”,内部类允许继承多个非接口类型
interface A{}interface B{}class X implements A,B{}class Y implements A{B makeB(){return new B() {}; //返回一个B的引用}}public class MultiInterfaces {static void takesA(A a){}static void takesB(B b){}public static void main(String[] args) {X x = new X();Y y = new Y();takesA(x);takesA(y);takesB(x);takesB(y.makeB());}}
如果拥有的时抽象类或者具体的类不是接口,那么只能使用内部类来实现多继承
class D{}abstract class E{}class Z extends D{E makeE(){return new E(){};}}public class MultiImplementation {static void takeD(D d){}static void takeE(E e){}public static void main(String[] args) {Z z = new Z();takeD(z);takeE(z.makeE());}}
内部类还有一些其他的特性
(1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类对象的信息相互独立
(2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承一个类
(3)创建内部类对象的时刻并不依赖外围类
内部类的继承
内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,哪个指向外围类对象的秘密引用必须被初始化,而在导出类中不再存在可连接的默认对象
class WithInner{class Inner{}}public class InheritInner extends WithInner.Inner { //只继承了内部类InheritInner (WithInner withInner){ //不能只传递一个指向外围类的对象//必须要在构造器中用super来初始化继承内部类的外围类withInner.super();}public static void main(String[] args) {final WithInner withInner = new WithInner();InheritInner inheritInner = new InheritInner(withInner);}}
内部类可以被覆盖吗
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 BigEgg();}}
当继承了某个外围类的时候,内部类并没有什么变换,这两个内部类时两个完全独立的实体
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) {final BigEgg bigEgg = new BigEgg();BigEgg.Yolk by = bigEgg.new Yolk();}}
class Egg2{protected class Yolk{public Yolk(){System.out.println("Egg2.Yolk()");}public void f(){System.out.println("Egg2.Yolk.f()");}}private Yolk yolk = new Yolk();public Egg2(){System.out.println("new Egg2()");}public void insertYolk(Yolk yolk1){yolk = yolk1;}public void g(){yolk.f();}}public class BigEgg2 extends Egg2{public class Yolk extends Egg2.Yolk{public Yolk(){System.out.println("BigEgg2.Yolk()");}public void f(){System.out.println("BigEgg2.Yolk.f()");}}public BigEgg2(){insertYolk(new Yolk());}public static void main(String[] args) {final Egg2 egg2 = new BigEgg2();egg2.g();}}
局部内部类
局部内部类一个典型的方式是在方法的方法体内创建,局部内部类中不能有访问说明符,因为它并不是外围类的一部分,但是它可以访问当前代码块的常量,还有他的外围类的所有成员
为什么使用局部内部类而不是匿名内部类?
我们需要一个构造器,或者重载构造器,但是匿名内部类中没有构造器,仅仅只有实例初始化起到了类似构造器的功能
interface Counter{int next();}public class LocalInnerClass {private int count =0;Counter getCounter(String name){ // 局部内部类在方法内部创建class LocalCount implements Counter{public LocalCount(){System.out.println("LocalCount()");}@Overridepublic int next() {System.out.print(name);return count++;}}return new LocalCount();}Counter getCounter2(String name){ //匿名内部类return new Counter() {{System.out.println("Counter()");}@Overridepublic int next() {System.out.print("Counter()");return count++;}};}public static void main(String[] args) {final LocalInnerClass localInnerClass = new LocalInnerClass();Counter c1 = localInnerClass.getCounter("LocalInner");Counter c2 = localInnerClass.getCounter2("AnonymousInner");for (int i = 0 ; i < 5 ; i++){System.out.println(c1.next()); //两个内部类中的next方法访问的是同一个count}for (int i = 0 ; i < 5 ; i ++){System.out.println(c2.next());}}}
