您的位置:首页 > 其它

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

2016-08-24 21:51 513 查看

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());
}

内部类可以有多个实例,每个实例都有自己的状态信息,并且与外围类相互独立。
在单个外围类中,可以让多个内部类以不同的方式实现同一个接口(多个内部类的实现)。
创建内部类的时刻并不依赖于外围类对象的创建。【这句好像与前面矛盾,楼主也不解】
内部类没有“is-a”关系,它就是一个实体。

2.闭包<
4000
/h2>
闭包是指可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。闭包包含的自由变量来自于定义闭包的环境。
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.局部内部类

在代码块里创建的内部类,比如在一个方法体内创建一个内部类
局部内部类不能有访问修饰词(private 等),因为它不是外围类的一部分。
可以访问当前代码块内的常量,以及此外围类的所有成员。

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));
}
}

总结:

内部类使得多重继承更加的完善。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: