Java编程思想:第10章 内部类

能够将一个类的定义放在另外一个类的内部,这就是内部类。java

1.能够实现隐藏闭包

2.内部类了解外围类,并能与之通讯,不少时候能够写出更加优雅和清晰的代码框架

10.1建立内部类this

public class Outer{spa

  class Inner{设计

  }code

}对象

若是想从外部类的"非静态方法以外"的任意位置建立某个内部类对象,那么必须具体指明这个对象的类型:OuterClassName.InnerClassName继承

10.2连接到外部类接口

内部类自动拥有对外围类全部成员的访问权,这是如何作到的呢?当外围类对象建立了一个内部类对象时,此内部类对象一定会秘密的捕获一个指向那个外围类对象的引用,在访问外部类对象成员时,就是用这个引用来访问。编译器会帮助咱们处理这些细节,而咱们使用时,就像是内部类自己带有的同样。因此非static内部类在建立时候必需要由外部类对象来建立。

10.3使用.this与.new

若是要使用外部类对象的引用,OuterClassName.this来获取,编译器会检查类型是否正确,无运行时开销。

建立内部类对象:要先有外部类对象Outer o = new Outer();而后用外部类对象建立内部类对象Outer.Inner i = o.new Inner();不能够用o.new Outer.Inner();

10.4内部类向上转型

当把private内部类向上转型成基类,尤为是接口的时候,内部类就有了用武之地:这个接口的实现能够彻底隐藏,彻底不可见,由于返回的是接口引用

参考java源码AbstractList类:

public Iterator<E> iterator() {

  return new Itr();

}

private class Itr implements Iterator<E> {

  ...

}

当咱们外界使用迭代器的时候

Iterator iter = arrayList.iterator();

只能拿到接口的引用,彻底不知道里面是一个private内部类,由于Itr是private的,也不能建立它。

10.5在方法和做用域内的内部类

局部内部类:定义在方法体或者某一做用域里,超出做用域不能使用。

注意:不能使用意味着在外部不能被访问到类型,可是该类型在JVM里是实际存在的,即便这个做用域已经结束。

10.6匿名内部类

没有名字的内部类,能够有带参构造器,能够添加并初始化域。

若是在内部类的内部要使用外部的一个对象,这个对象的引用须要是final的。构造器中使用的参数不须要是final的。

匿名内部类里不能本身添加构造器(由于不能被访问到,并且没名字),须要作一些初始化的时候,能够用代码块来实现。

匿名内部类与正规的继承相比,要么扩展类,要么实现接口,不能两者都有,而且只能实现一个接口。

10.6.1再访工厂方法

能够把工厂方法改为匿名内部类。

interface Game{boolean move();}

interface GameFactory{Game getGame();}

class Checkers implements Game{

  private Checkers(){}

  private int moves = 0;

  private static final int MOVES = 3;

  public static GameFactory factory = new GameFactory(){

    public Game getGame(){return new Checkers();}  

  };

  public boolean move(){

    return ++moves != MOVES;

  }

}

10.7嵌套类-静态内部类

1)建立静态内部类对象不须要外部类对象

2)不能从静态内部类对象访问外围内的非静态成员

普通内部类不能够有static字段,静态能够有

10.7.1接口内部的类

正常状况下,接口内部是不能听任何代码的,可是嵌套类能够放。放入接口中的任何类都是public static的。

用途:若是你想建立某些公共代码,使得它们能够被这个接口的全部实现使用,那么接口中嵌套内部类就会很是方便。

10.7.2在多层嵌套类中访问外部类成员

一个内部类被嵌套多少层并不重要,它能够透明的访问全部外围类的全部成员。

class MMA{

  private void f(){}

  class A{

    private void g(){}

    public class B{

      void h(){

        f();//直接调用 没有问题

        g();

      }

    }

  }

}

10.8为何须要内部类

通常来讲,内部类继承某个类或实现某个接口,而且能够操做外围类对象。

核心缘由:内部类能够提供多重继承的能力。

好比: AbstractList里有Itr和ListItr两种迭代器,若是咱们只是让AbstractList实现Iterator接口,那么只能写出一个迭代器,经过使用内部类方式能够添加多个迭代器。

10.8.1闭包与回调

若是有一个类须要实现一个接口来提供某个方法,可是发现这个方法已经被另一个接口使用了,那么能够用内部类代替这个类去实现接口。

回调的价值在于它的灵活性---能够在运行时动态决定须要调用什么方法。

10.8.2内部类与控制框架

设计一个控制框架,要求到了设定时间就执行相应的事件:

//建立一个抽象事件类,有执行时间和延迟时间,在产生新事件的时候用延迟时间计算出执行时间,action方法由实现类实现。

public abstract class Event{

  private long eventTime;

  protected final long delayTime;

  public Event(long delay){

    this.delayTime = delay;

    start();  

  }

  public void start(){

    eventTime = System.nanoTime() + delayTime;

  }

  public boolean ready(){

    return System.nanoTime >= eventTime;

  }

  public abstract void action();

}

//建立一个控制类,循环遍历全部事件,若是事件时间到了就执行action

public class Controller{

  private List<Event> eventList = new ArrayList<>();

  public void addEvent(Event e){eventList.add(e);}

  public void run(){

    while(eventlist.size()>0){

      for(Event e : eventList){

        if(e.ready){

          e.action();

          eventlist.remove();

        }

      }

    }

  }

}

从上面这2个类的设计来看,咱们彻底不知道event具体作什么,可是咱们整个控制框架的核心功能:"把变化的事物(event具体实现),与不变的事物(时间发生过程)"分开。而咱们只须要建立不一样的变化的Event,这正是内部类要作的事情:

1)控制框架里的实现是由单个类建立的,从而使得实现细节被封装起来。内部类用来表示解决问题所必须的不一样action。

2)内部类能够很容易访问外部类成员,这种实现不会显得很笨拙。

控制框架:

public class GreenhouseControls extend Controller{

  private boolean light = false;

  public class LightOn extends Event{

    public LightOn(long delay){super(delay);}

    public void action(){

      //hardware control code to turn on light

      light = true;

    }

  }

  public class LightOff extends Event{..}

  private boolean water = false;

  public class WaterOn extends Event{

    public WaterOn(long delay){super(delay);}

    public void action(){

      //..

      water = true;

    }

  }

  //...其余内部类

}

控制框架中用内部类把实现细节都隐藏起来了。

那么如何使用这个框架并执行须要的事件呢:

public static void main(String[] args){

  GreenhouseControls gc = new GreenhouseControls();

  gc.addEvent(gc.new LightOn(100));

  gc.addEvent(gc.new LightOff(200));

  ...

  gc.run();

}

控制框架能够体现出内部类的价值,图形界面设计里更是让人信服。

10.9内部类的继承

 内部类的构造器必须链接到外围类对象的引用,若是一个类继承了内部类,那么如何保证外围类对象存在呢?须要使用特殊语法:

1.在导出类添加一个带外围类参数的构造器

2.构造器里用outer.super()来提供外围类引用

例如:

class Outer{

  class Inner{}

}

public class InheritInner extends Outer.Inner{

  InheritInner(Outer o){

    o.super();

  }

}

10.10内部类能够被覆盖吗

class Outer{

  class Inner{}

}

class Outer2 extends Outer{

  class Inner{}

}

在Outer里调用Inner并不会被Outer2里的Inner覆盖,即不会产生多态特性。

10.11局部内部类

在方法体里使用局部内部类和使用匿名内部类有什么区别呢?

惟一的区别在于:局部内部类能够有命名的构造器,而且能够被重载用来初始化,能够产生多个对象。而匿名内部类只能提供一个该类型的对象。

10.12内部类标识符

匿名内部类: Outer类名 $ 数字

成员内部类:  Outer类名 $ 内部类名

局部内部类: Outer类名 $ 数字 内部类名

10.13总结

接口和内部类实现了C++的多重继承功能,可是更优雅,更简洁。

相关文章
相关标签/搜索