第十章 内部类 内部类的作用、闭包、内部类继承、覆盖重写内部类、局部内部类、内部类标识符

1.为什么需要内部类

  • 内部类继承自某个类或者实现某个接口,内部类可以访问创建它的外部类的对象或参数资源。
  • 内部类能独立的实现某个接口,无论外围类是否实现了这个接口。
  • 内部类使多重继承的解决方案变得更加完善。
    下面的代码体现内部类实现多重继承,实现多个接口

    interface A{}
    interface B{}
    
    //实现两个接口的单一类
    class X implements A,B{}
    //实现两个接口的存在内部类的类
    class Y implements A{
            //在内部类实现另一个B接口
    	B makeB(){
    		//返回一个内部类
    		return new 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();
    		/**
    		 * 下面可以看到x和y都可以实现传入takes.()方法中,而这是两个接口
    		 */
    		takesA(x);
    		takesA(y);
    		takesB(x);
    		//获取内部类并传入takesB()
    		takesB(y.makeB());
    	}
    }
    
    从实现的观点来看,上面的外部类实现多重继承和内部类实现多重继承并没有什么区别
  • 上面是接口,所以实现多重继承没有问题,但如果要对抽象类或实际类实现多重继承,就需要使用内部类来实现

    class D{}
    abstract class E{}
    //一个外部类继承了D就不能再继承其他的类了
    class Z extends D{
    	//内部类实现多重继承
    	E makeE(){return new E(){};}
    }
    public class MultiImplementation {
    	static void takesD(D d){}
    	static void takesE(E e){}
    	public static void main(String[] args) {
    		Z z = new Z();
    		takesD(z);
    		//获取Z中的E内部类对象
    		takesE(z.makeE());
    	}
    
    
  1. 内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类相互独立。
  2. 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口(多个内部类的实现)。
  3. 创建内部类的时刻并不依赖于外围类对象的创建。【这句好像与前面矛盾,楼主也不解】
  4. 内部类没有“is-a”关系,它就是一个实体。

2.闭包

  • 闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。闭包包含的自由变量来自于定义闭包的环境。
  • java中的闭包是一个对象,它记录了一些信息,这些信息来自于创建它的作用域。
  • 通过闭包实现java中的回调
    interface Incrementable{
    	void increment();
    }
    //外围类实现一个接口
    class Callee1 implements Incrementable{
    	private int i = 0;
    	//回调方法
    	@Override
    	public void increment() {
    		i++;
    		System.out.println(i);
    	}
    }
    class MyIncrement{
    	//一个同上面接口中的方法相同的方法
    	public void increment(){System.out.println("Other operation");}
    	static void f(MyIncrement mi){mi.increment();}
    }
    
    //继承自上面的MyIncrement,因为MyIncrement中的方法与Incrementable接口中方法相同,实现回调接口的功能就不可以用普通的方式实现了
    class Callee2 extends MyIncrement{
    	private int i = 0;
    	public void increment() {
    		super.increment();
    		i++;
    		System.out.println(i);
    	}
    	//一个内部类,实现了一个回调接口,这是一个<span style="color:#FF0000;">闭包</span>,信息来自于创建它的作用域
    	private class Closure implements Incrementable{
    		//回调方法,也叫钩子
    		@Override
    		public void increment() {
    			/**
    			 * 执行外部类的方法,这样只要其他的对象获取到这个内部类接口,就可以调用这个回调方法,回调方法里面调用外部类的private参数,
    			 * 实现与外界的相通
    			 */
    			Callee2.this.increment();
    			//使用一个private
    			i ++;
    		}
    	}
    	//返回一个回调接口,与外界相联系
    	Incrementable getCallbackReference(){
    		return new Closure();
    	}
    }
    class Caller{
    	private Incrementable callbackReference;
    	public Caller(Incrementable cbh) {callbackReference = cbh;}
    	void go(){callbackReference.increment();}
    	
    }
    public class Callbacks {
    	public static void main(String[] args) {
    		Callee1 c1 = new Callee1();
    		Callee2 c2 = new Callee2();
    		MyIncrement.f(c2);
    		//传入一个实现了回调接口的对象
    		Caller caller1 = new Caller(c1);
    		//得到Callee2内部类声明的回调接口,并传入Caller对象,供Caller对象回调接口中的方法
    		Caller caller2 = new Caller(c2.getCallbackReference());
    		//执行回调
    		caller1.go();
    		caller1.go();
    		caller2.go();
    	}
    }
    
    在Android中,闭包回调多用于Activity和其Fragment之间的传参,当然adapter有时也会与Activity传参使用回调接口的方法。
  • 回调的价值在于它的灵活性,可以在程序运行时动态的决定调用什么方法。

3.内部类与继承

  • 集成过程中的问题在于:内部类的构造器必须链接到只想外围类对象的引用,而到导出类(子类)不存在可连接的默认对象引用。
    要解决这个问题,必须使用特殊的语法
    class WithInner{
    	class Inner{}
    }
    //继承一个内部类
    public class InneritInner extends WithInner.Inner{
    	public InneritInner(WithInner wi) {
    		/**
    		 * 构造期内必须引用所继承的外围类对象
    		 * 编译器才不会报错
    		 */
    		wi.super();
    	}
    	public static void main(String[] args) {
    		//调用的时候,总之内部类必须与其外围类链接才能使用,即使继承
    		WithInner wi = new WithInner();
    		InneritInner ii = new InneritInner(wi);
    	}
    }
    

4.覆盖(重写)内部类,像重写方法一样

  • 子类可不可以覆盖父类中的内部类呢
    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();
    	}
    }
    /**
     *输出:未达到想要的情况,未出现覆盖情况
     *New Egg()
     *Egg.Yolk()
     */
  • 当继承某个外围类的时候,内部类并没有发生特别的变化。这两个内部类是完全独立的两个实体,在各自的命名空间内。
  • 明确的继承某个内部类,子类内部类继承父类的内部类,构造器的执行过程是先执行外围类的,再执行相关内部类的
    class Egg2{
    	protected class Yolk{
    		public Yolk(){System.out.println("Egg2.Yolk()");}
    		public void f(){System.out.println("Egg2.Yolk.f()");}
    	}
    	private Yolk y = new Yolk();
    	public Egg2(){System.out.println("New Egg2()");}
    	public void insertYolk(Yolk yy){y = yy;}
    	public void g(){y.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) {
    		Egg2 e2 = new BigEgg2();
    		e2.g();
    	}
    }
    /**
     * Egg2.Yolk() //这儿很奇怪,其实是先初始化最终父类的class域内的变量,这个是Egg2中的private Yolk y = new Yolk();
     * New Egg2() 
     * Egg2.Yolk() 
     * BigEgg2.Yolk() 
     * BigEgg2.Yolk.f()
     * 
     */
    

5.局部内部类

  • 在代码块里创建的内部类,比如在一个方法体内创建一个内部类
  1. 局部内部类不能有访问修饰词(private 等),因为它不是外围类的一部分。
  2. 可以访问当前代码块内的常量,以及此外围类的所有成员。
  3. 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");
    			}
    			@Override
    			public int next() {
    				System.out.println(name);
    				return count ++;
    			}
    		}
    		//返回此局部内部类
    		return new LocalCounter();
    	}
    	Counter getCounter2(final String name){
    		//匿名内部类
    		return new Counter() {
    			//匿名内部类的高仿构造器
    			{System.out.println("Counter");}
    			@Override
    			public int next() {
    				System.out.println(name);
    				return count ++;
    			}
    		};
    	}
    	public static void main(String[] args) {
    		LocalInnerClass lic = new LocalInnerClass();
    		Counter 
    			c1 = lic.getCounter("Loc 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());
    	}
    }
    局部内部类的名字在方法外是不可见的,和匿名内部类没有名字是一样的,那为什么要用局部内部类??
    唯一的理由是内部类需要一个已命名的构造器或者重写构造器,而匿名内部类并不能实现构造器,它只能用于实例初始化。

    另一个理由是需要不止一个该内部类的对象。

6.内部类标识符

  • 每个类都会产生一个.class文件,其中包含了如何创建该类的对象的全部信息。
  • 内部类也必须生成一个.class文件已包含他们的Class对象信息。
  • 类文件的命名有严格的规则。
    外围类的名字,加上$,在加上内部类的名字。例如LocallnnerClass生成的.class文件包括:
    Counter.class
    LocalInnerClass$1.class
    LocalInnerClass$1LocalCounter.class
    LocalInnerClass.class
  • 如果内部类是匿名的,编译器会简单的产生一个数字作为其标识符。
    如果内部类是嵌套在别的内部类之中,只需直接将它们的名字简单加在其外围类标识符与$的后面

7.应用程序框架

  • 应用程序框架——解决特定的问题的框架。
  • 应用程序框架就是被设计用以解决某类特定问题的一个类或一组类。
  • 要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。
    在覆盖的方法中编写代码定制应用程序提供的通用解决方案,以解决特定问题。——模板方法设计模式。

    设计模式总是将变化的事物和不变的事物分离开来。其中模板方法是保持不变的事物,可覆盖的方法就是变化的事物。

8.内部类和控制框架

  • 控制框架是一类特殊的应用程序框架,用来解决响应事件的需求。
  • 驱动系统:用来响应事件的系统。
    public abstract class Event {
    	private long eventTime;
    	protected final long delayTime;
    	public Event(long delayTimg){
    		this.delayTime = delayTimg;
    		start();
    	}
    	/**
    	 * 启动计时器的方法,调用此方法可以重新启动计时器,也能够重复使用Event对象。
    	 * 重复一个事件,就在action中调用start()方法
    	 */
    	public void start(){
    		 eventTime = System.nanoTime() + delayTime;
    	}
    	//何时可运行action()方法。覆盖此方法,实现Event除基于事件以外的其他因素出发action方法
    	public boolean ready(){
    		return System.nanoTime() >= eventTime;
    	}
    	//动作执行方法
    	public abstract void action();
    }
    public class GreenHouseControls extends Controller{
    
    	private boolean light = false;
    	//一个内部类 继承Event事件类控制开灯
    	public class LightOn extends Event{
    		public LightOn(long delayTimg) {super(delayTimg);}
    		@Override
    		public void action() {
    			light = true;
    		}
    		public String toString(){return "Light is on";}
    	}
    	//一个内部类 继承Event事件类控制开灯
    	public class LightOff extends Event{
    		public LightOff(long delayTimg) {super(delayTimg);}
    		@Override
    		public void action() {
    			light = false;
    		}
    		public String toString(){return "Light is off";}
    	}
    }
    public class GreenhouseController {
    
    	public static void main(String[] args) {
    		GreenHouseControls gc = new GreenHouseControls();
    		//添加一个事件 1秒后执行
    		gc.addEvent(gc.new LightOff(1000));
    	}
    }
    

总结:

  • 内部类使得多重继承更加的完善。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值