内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类。
使用代码来举几个栗子
内部类的基本语法
public class Parcel {
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 disc) {
Contents c = new Contents();
Destination d = new Destination(disc);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel p = new Parcel();
p.ship("Tismania");
}
}
当我们在ship()方法中使用内部类时,与不同类并没有什么不同。在这里,实际的区别只是将类嵌套在Parce1中。
更经典的情况是,外部类有一个方法,这个方法返回一个内部类的引用。
** 栗子:**
public class Parcel2 {
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 Destination to(String s) {return new Destination(s);}
public Contents content() {return new Contents();}
public void ship(String disc) {
Contents c = new Contents();
Destination d = new Destination(disc);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("tasmanias");
Parcel2 q = new Parcel2();
Parcel2.Contents c = q.content();
// c = new Contents(); //内部类不能直接创建自己的实例,必须通过外部类具体的指名这个对象的类型
Parcel2.Destination d = q.to("Borneo");
}
}
** 如果想从外部类的非静态方法之外的任意位置创建某个内部类的实例,必须像main()方法中那样,具体的指名这个对象的类型 **
到目前为止,内部类只是一种名字隐藏和组织代码的模式。其实内部类还有其他的用途。当生成一个内部类的对象时,此对象就与生成它的外围类之间有了一种联系,所以它能访问外围类的所有元素
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(String s) {
if (next < items.length) { // 数组还有空间,则在末尾追加新的object
items[next] = s;
next++;
}
}
@SuppressWarnings("unused")
private class SequenceSelector implements Selector {
private int i = 0;
@Override
public boolean end() {
return i == items.length;
}
@Override
public Object current() {
return items[i];
}
@Override
public void next() {
if (i < items.length)
i++;
}
}
public Selector selector() {
return new SequenceSelector();
}
public static void main(String[] args) {
Sequence sequence = new Sequence(10);
for (int i = 0; i < 10; i++)
sequence.add(Integer.toString(i));
Selector selector = sequence.selector();
while (!selector.end()) {
System.out.print(selector.current() + " ");
selector.next();
}
}
}
** Squence类只是固定最大小的Object数组,已类的形式包装起来,add()方法可以序列末尾添加新的Object(只要还有空间)。Selector中的方法:end()检查序列是否到了末尾,current()返回当前对象,以及next()移动到序列中的下一个对象 **
** 最初看到SequenceSelector,可能会觉得只不过是另一个内部类罢了,但仔细观察end()、current()、next()方法都用到了Object,这只是一个引用,它并不是SequenceSelector的一部分,而是外围类中的字段,而内部类可以访问到外围类中的成员 **
所以内部类自动拥有对其外围类所有成员的访问权。这是怎么样做到的?
当一个外围类创建一个内部类的实例时,该实例会秘密的捕获一个外围类对象的引用。然后,在你访问此外围类成员时,就是用秘密捕获的引用来选择外围类的成员。
.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 dot = new DotThis();
DotThis.Inner inner = dot.inner();
inner.outer().f();
}
}
创建内部类对象也可以使用.new语法创建
public class DotNew {
public class Inner{}
public static void main(String[] args) {
DotNew dot = new DotNew();
DotNew.Inner inner = dot.new Inner();
}
}
想要创建内部类的对象,不能直接引用外部类的名字DotNew,而是必须使用外部类的对象创建该内部类的对象。
在拥有外部类对象之前是不能创建内部类的对象的,这是因为内部类对象会暗暗连接在创建它的外部类对象上,在外部类没有创建的时候,内部类是不可能被创建的。
在方法和作用域中的内部类
上面所写的是内部类的典型用途,这是较普通的内部类,还有在方法里面和作用域内定义的内部类。
定义在方法中
public class Parcel5 {
public Destination destination(String s) {
class PDestination implements Destination{
private String label;
private PDestination(String whereTo){
label = whereTo;
}
@Override
public String readLabel() {
return label;
}
}
return new PDestination(s);
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
}
}
PDestination类是destination()方法的一部分,而不是Parcel5。所以在destination()方法之外不能访问到。
定义作用域中
public class Parcel6 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip{
private String id;
public TrackingSlip(String s) {
id = s;
}
String getSlip() {
return id;
}
}
TrackingSlip ts = new TrackingSlip("slip");
String s = ts.getSlip();
System.out.println(s);
}
// TrackingSlip ts1 = new TrackingSlip("x"); //超出if作用域的范围,不能创建TrackingSlip对象
}
public void track() {internalTracking(true);}
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}
TrackingSlip类嵌在if作用域中,并不是说该类的创建是有条件的,它其实与其他别的类一起被编译过了。然而,在定义TrackingSlip类的作用域之外,它是不能被访问到的。
匿名内部类
public class Parcel7 {
public Contents content() {
return new Contents(){
private int i = 11;
@Override
public int value() {
return i;
}
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c= p.content();
System.out.println(c.value());
}
}
** content()方法将返回值的生成与表示这个返回值的类的定义结合在一起!另外,这个类是匿名的,它没有名字。 **
** 这种奇怪的语法指的是:“创建一个继承自Contents类的对象”,通过new表达式返回的引用自动向上转型为对Contents的引用。这种语法其实是下述形式的简化: **
public class Parcel7b {
class MyContents implements Contents{
private int i = 11;
@Override
public int value() {
return i;
}
}
public Contents content() {
return new MyContents();
}
public static void main(String[] args) {
Parcel7b p = new Parcel7b();
Contents c= p.content();
System.out.println(c.value());
}
}
这个匿名内部类中,使用了默认的构造器来生成Contents。还可以使用有参构造器生成Contents:
public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) {
@Override
public int value() {
return super.value()*47;
}
};
}
public static void main(String[] args) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(10);
System.out.println(w.value());
}
}
这里的Wrapping只是一个具有具体实现的的普通类,但它还是被导出类当公共"接口"使用。
** 如果想做一些类似构造器的行为,该怎么办呢?在匿名类中不可能有匿名构造器(因为它根本没有名字!),其实可以通过实例初始化,就能达到为匿名内部类创建一个构造器的效果: **
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) {
return new Base(i) {
{System.out.println("Inside instance init");}
@Override
public void f() {
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}
使用内部类实现工厂模式
** 工厂模式是开发中十分常见的设计模式,如果使用内部类实现工厂模式,会让代码变得非常简洁: **
interface Service{
void method1();
void method2();
}
interface ServiceFactory{
Service getService();
}
class Implementsation1 implements Service{
private Implementsation1() {}
@Override
public void method1() {
System.out.println("Implementsation1 method1");
}
@Override
public void method2() {
System.out.println("Implementsation1 method2");
}
public static ServiceFactory factory =
new ServiceFactory() {
@Override
public Service getService() {
return new Implementsation1();
}
};
}
class Implementation2 implements Service{
private Implementation2() {}
@Override
public void method1() {
System.out.println("Implementation2 method1");
}
@Override
public void method2() {
System.out.println("Implementation2 method2");
}
public static ServiceFactory factory =
new ServiceFactory() {
@Override
public Service getService() {
return new Implementation2();
}
};
}
public class Factories {
public static void getService(ServiceFactory fact) {
Service ser = fact.getService();
ser.method1();
ser.method2();
}
public static void main(String[] args) {
getService(Implementsation1.factory);
}
}
现在用于Implementsation1和Implementsation2的构造器都可以是private的,并且没有任何必要创建工厂的具体类。这样只需要单一的工厂对象,因此被创建成Service实现的static域,这样产生的语法更具有实际意义。