JAVA编程思想学习总结:第十章接口内部类
2015-05-25 14:44
459 查看
(1)内部类的使用
public class DoThisNew { void f(){System.out.println("DoThisNew.f()");} public class InnerThis{ public DoThisNew outer(){ return DoThisNew.this;//在内部类中使用this,现在该this指针指向其父类 } public InnerThis innerer(){ return this;//在内部类中使用this,现在该this指针指向其本身 } void f(){System.out.println("InnerThis.f()");} } public class InnerNew{} public InnerThis innerThis(){return new InnerThis();} public static void main(String[] args){ DoThisNew dt =new DoThisNew(); DoThisNew.InnerThis dt1=dt.innerThis(); dt1.outer().f(); dt1.innerer().f(); DoThisNew.InnerNew dn2=dt.new InnerNew();//创建内部类的方式.new,以及内部类指针的声明方式 } }以上代码展示了如何在内部类中使用this指针,以及在外部创建内部类的方式。
interface Destination{ String readLabel(); } interface Contents{ int value(); } class Parcel4{ private class PContents implements Contents{ private int i=11; public int value(){return i;} } protected class PDestination implements Destination{ private String label; private PDestination(String whereTo){ label =whereTo; } public String readLabel(){return label;} } public Destination destination(String s){ return new PDestination(s); } public Contents contents(){ return new PContents(); } } public class TestParcel { public static void main(String[] args){ Parcel4 p=new Parcel4(); Contents c=p.contents(); Destination d=p.destination("Tasmania"); // Illegal --can't access private class; // !Parcel4.PContents pc = p.new PContents(); } }在此处,我们对内部类做了特别的限制,即内部类是private或者public的。它们实现了某一接口,创建它们实对它们做了向上的转型。对这些类的访问受到了一定的限制,比如不能将私有内部类向下转型,受保护的内部类也只能向下转型成继承自它的子类。所以私有内部类给类的设计者提供了一种途径,通过 这种方式可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。此外,从客户端程序员的角度来看,由于不能访问任何新增加的,原本不属于公共接口的方法,所以扩展接口是没有价值的。这也给了Java编译器提供了生成更高效代码的机会。
内部类还可以在一个方法里面或者任意的作用域内定义内部类。这么做有两个理由:
1)如前所示,实现了某类型的接口,于是可以创建并返回对其的引用。
2)要解决一个复杂的问题,创建一个类来辅助你的解决方案,但是又不希望这个类是公共可用的。
(2)匿名内部类
/* * P198-199 */ abstract class Base{ public Base(int i){ System.out.println("Base constructor. i="+i); } public abstract void f(); } public class AnonymousConstructor { public static Base getBase(int i,final String dest){//在内部类中直接使用的外部参数必须定义为final,但是传递到基类构造器中的变量并不需要 return new Base(i){ {System.out.println("Inside instance initializer");}//代码并不能作为实例初始化的一部分开始执行 public void f(){ System.out.println("In anonymous f()"); } private String label=dest; {System.out.println("Inside label "+label);} };//该分号只是作为返回语句的结束,并不是匿名内部类的声明需要这个分号 } public static void main(String[] args){ Base base = getBase(47,"123456"); base.f(); } }
以上代码展示了匿名内部类的创建语法,包括一部分限制,如在内部类中使用的外部变量必须要声明为final,内部类中的代码必须包含在一对中括号内,以作为实例初始化的一部分。
匿名类的实例初始化实际效果就是构造器。当然这受到了限制——不能重载实例初始化方法,因为仅有一个这样的构造器。
匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备,且只能实现一个接口。
/* * P200-201 */ 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 boolean move(){ System.out.println("Checkers move "+ moves); return ++moves !=MOVES; } public static GameFactory factory = new GameFactory(){ public Game getGame(){return new Checkers();} }; } class Chess implements Game{ private Chess(){} private int moves =0; private static final int MOVES=4; public boolean move(){ System.out.println("Chess move "+moves); return ++moves!=MOVES; } public static GameFactory factory =new GameFactory(){ public Game getGame(){return new Chess();} }; } public class Games { public static void playGame(GameFactory factory){ Game s=factory.getGame(); while(s.move()){ } } public static void main(String[] args){ playGame(Checkers.factory); playGame(Chess.factory); } }这是通过匿名类实现工厂模式的一个例子。在代码中,将构造器声明为private,并且没有创建作为工厂的具名类,因为没有必要。另外,需要的往往只是单一的工厂对象,所以本例中被创建为一个static域。
(3)嵌套类
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这通常称为嵌套类。嵌套类意味着:1)要创建嵌套类的对象,并不需要其外围类的对象。2)不能从嵌套类的对象中访问非静态的外围类对象。嵌套类与普通的内部类还有一个区别,普通内部类的字段与方法,只能入在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西。嵌套类并没有特殊的this引用以指向其外围类对象,这使得它类似于一个static方法。
嵌套类可以做为接口的一部分。放到接口中的任何类都自动地是public和static的,将嵌套类置于接口的命名空间内,并不违反接口的规则,甚至可以在内部类中实现其外围接口。
如果想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部嵌套类会显得很方便。
一个内部类被嵌套多少层并不重要,因为它能透明地访问所有它所嵌入的外围类的所有成员。
(4)内部类的作用
一般来说,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。内部类实现一个接口与外围类实现这个接口的区别在于外围类不是总能享用到接口带来的方便,有是需要用到接口的实现。但是每个内部类都能独立地继承自一个(接口的)实现,无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类有效的实现了多重继承。
除了多继承,内部类还有一些其它特性:1)内部类可以有多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。2)在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。3)创建内部类时刻并不依赖于外围类对象的创建。4)内部类并没有令人迷惑的"is-a"关系;它就是一个独立的实体。
1、闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。由此可见,内部类是面向对象的闭包,因为它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用。在此作用域内,内部类有权操作所有的成员,包括private成员。通过内部类提供闭包的功能是优良的解决方案。
/* * P206-207 */ interface Incrementable{ public void increment(); } class Callee1 implements Incrementable{ private int i= 0; 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(); } } // If your class must implement increment() in // some other way. you must use an inner class; class Callee2 extends MyIncrement{ private int i=0; public void increment(){ super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable{ public void increment(){ //Specify outer-class method ,otherwise //you'd get an infinite recursion; Callee2.this.increment(); } } Incrementable getCallbackReference(){ return new Closure(); } } class Caller{ private Incrementable callbackReference; 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); Caller caller2=new Caller(c2.getCallbackReference()); caller1.go(); caller1.go(); caller2.go(); caller2.go(); } }回调的价值在于它的灵活性——可以在运行时动态地决定需要调用什么方法。
2、内部类与控制框架
应该程序框架就是被设计用以解决某类特定问题的一个类或一组类。要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决你的特定问题(这是设计模式中模板方法的一个例子)。模板方法包含算法的基本结构,并且会调用一个或多个可覆盖的方法,以完成算法的动作。设计模式总是将变化的事物与保持不变的事物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求,主要用来响应事件和系统被称作事件驱动系统。应用程序设计中常见的问题之一是图形用户接口(GUI),它几乎完全是事件驱动的系统。
/* * P208-211 */ //The common methods for any control event. abstract class Event{ private long eventTime; protected final long delayTime; public Event (long delayTime){ this.delayTime=delayTime; start(); } public void start(){//Allows restarting eventTime=System.nanoTime()+delayTime; } public boolean ready(){ return System.nanoTime()>=eventTime; } public abstract void action(); } //The reusable framework for control systems. class Controller{ //A class from java.util to hold Event objects; private List<Event> eventList=new ArrayList<Event>(); public void addEvent(Event c){ eventList.add(c); } public void run(){ while(eventList.size()>0){ //Make a copy so you're not modifying the list //while you're selection ghe elements in it; for(Event e:new ArrayList<Event>(eventList)){ if(e.ready()){ System.out.println(e); e.action(); eventList.remove(e); } } } } } //This produces aspecific application of the //control system, all in a single class. Inner //classes allow you to encapsulate different //functionality for each type of event. class GreenhouseControls extends Controller{ private boolean light=false; public class LightOn extends Event{ public LightOn(long delayTime){super(delayTime);} public void action(){ //Put hardware control code here to //physically turn on the light. light=true; } public String toString(){return "Light is on";} } public class LightOff extends Event{ public LightOff(long delayTime){super(delayTime);} public void action(){ //Put hardware control code here to //physically turn off the light. light=false; } public String toString(){return "Light is off";} } private boolean water = false; public class WaterOn extends Event{ public WaterOn(long delayTime){super(delayTime);} public void action(){ //Put hardware control code here; water = true; } public String toString(){return "Greenhouse water is on";} } public class WaterOff extends Event{ public WaterOff(long delayTime){super(delayTime);} public void action(){ //Put hardware control code here; water=false; } public String toString(){return "Greenhouse water if off";} } private String thermostat="Day"; public class ThermostatNight extends Event{ public ThermostatNight(long delayTime){super(delayTime);} public void action(){ //Put hardware control code here; thermostat="Night"; } public String toString(){return "Thermostat on night setting";} } public class ThermostatDay extends Event{ public ThermostatDay(long delayTime){ super(delayTime); } public void action(){ //Put hardware control code here; thermostat="Day"; } public String toString(){ return "Thermostat on day settion"; } } //An example of an action() that inserts a //new one of itself into the event list; public class Bell extends Event{ public Bell(long delayTime){super(delayTime);} public void action(){addEvent (new Bell(delayTime));} public String toString(){return "Bing!";} } public class Restart extends Event{ private Event[] eventList; public Restart(long delayTime,Event [] eventList){ super(delayTime); this.eventList=eventList; for(Event e:eventList) addEvent(e); } public void action(){ for(Event e:eventList){ e.start();//return each event addEvent(e); } start();//return this Event addEvent(this); } public String toString(){return "Restarting system";} } public static class Terminate extends Event{ public Terminate(long delayTime){super(delayTime);} public void action(){System.exit(0);} public String toString(){return "Terminating";} } } public class GreenhouseController { public static void main(String[] args){ GreenhouseControls gc=new GreenhouseControls(); //Instead of hard-wiring.you could parse //configuration information from a text file here; gc.addEvent(gc.new Bell(900)); Event[] eventList={ gc.new ThermostatNight(0), gc.new LightOn(200), gc.new LightOff(400), gc.new WaterOn(600), gc.new WaterOff(800), gc.new ThermostatDay(1400) }; gc.addEvent(gc.new Restart(2000,eventList)); gc.addEvent(new GreenhouseControls.Terminate(1000000));//该处不同的参加会导致run中的循环运行不同的次数。 gc.run(); } }这是命令设计模式的一个例子。
(5)内部类的继承与覆盖
class WithInner{ class Inner{} } public class InheritInner extends WithInner.Inner{ /*InheritInner(){ //enclosingClassReference.super(); }//won't compile*/ InheritInner(WithInner wi){ wi.super(); } public static void main(String[] args){ WithInner wi=new WithInner(); InheritInner ii=new InheritInner(wi); } }可以看到,InheritInner只继承自内部类,而不是外围类。但是当要生成一个构造器时,默认的构造器并不算好,岊且不能只是传递一个指向外围类对象的引用。此外,必须在构造器内使用如下语法:enclosingClassReference.super();这样才提供了必要的引用,然后程序才能编译通过。
当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。
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()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
(6)局部内部类
局部内部类不能有访问说明符,因为它不是外围类的一部分,但是它可以访问当前代码块内的常量,以及此外围类的所有成员。interface Counter{ int next(); } public class LocalInnerClass { private int count = 0; Counter getCounter(final String name){ //A local inner class; class LocalCounter implements Counter{ public LocalCounter(){ //Local inner class can have a constructor System.out.println("LocalCounter()"); } public int next(){ System.out.println(name); return count++; } } return new LocalCounter(); } //The same thing with an anonymous inner class; Counter getcounter2(final String name){ return new Counter(){ //Anonymous inner class cannot have a named //constructor,only an instance initializer; { System.out.println("Counter()"); } public int next(){ System.out.println(name); return count++; } }; } public static void main(String[] args){ LocalInnerClass lic =new LocalInnerClass(); Counter c1=lic.getCounter("Local 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()); } }使用局部内部类而不使用匿名内部类的唯一理由是我们需要一个已命名的构造器或者需要重载构造器。
相关文章推荐
- JAVA编程思想学习总结:第9章接口
- JAVA编程思想学习总结:第21章第1-2节基本的线程机制
- JAVA编程思想学习总结:第21章第3节共享受限资源
- JAVA编程思想学习总结:第21章第4节终结与协作
- JAVA编程思想学习总结:第八章——多态
- JAVA编程思想学习总结:第十一章持有对象
- JAVA编程思想学习总结:第十三章字符串
- JAVA编程思想学习总结:第十四章类型信息
- 【java编程思想--学习笔记(三)】访问控制-接口实现与类的访问权限
- JAVA编程思想学习总结:第五章——初始化与清理
- JAVA编程思想学习总结:第七章——复用类
- Java编程思想学习笔记-第九章 接口
- Java编程思想——第六章 访问权限控制 学习总结
- Java编程思想-接口总结
- 经典内部排序算法学习总结(算法思想、可视化、Java代码实现、改进、复杂度分析、稳定性分析)
- Java编程思想 一二章学习总结
- Java编程思想 五六章学习总结
- Java编程思想 七八章学习总结
- java8学习总结——函数式接口@FunctionalInterface
- java8学习总结——Collector接口