一、 内部类特性
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。
- 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 。
- 内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量 。
二、 匿名内部类
1.
1 public class Parcel7 2 { 3 public Contents contents() 4 { 5 return new Contents() 6 { 7 private int i = 11; 8 public int value() { return i; } 9 };10 }11 public static void main(String[] args)12 {13 Parcel7 p = new Parcel7();14 Contents c = p.contents();15 }16 }
在上述代码中, contents() 方法将返回值的生成与表达这个返回值的类定义结合在一起。另外,这个类是匿名的,它没有名字。
语法:创建一个继承自Contents的匿名类的对象。通过new表达式返回的引用被自动向上转型为对Contents的引用。(向上转型内容参照:)
关于分号的说明:在匿名内部类末尾的分号,并不是用来标记此内部类结束的。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类,因此,这与别的地方使用的分号是一致的。
上述匿名内部类的代码,是下面代码的简化版本:
1 public class Parcel7 2 { 3 public MyContents implements contents 4 { 5 private int i = 11; 6 public int value() { return i; } 7 } 8 public Contents contents() { return new MyContents(); } 9 public static void main(String[] args)10 {11 Parcel7 p = new Parcel7();12 Contents c = p.contents();13 }14 }
在这个匿名类中,使用了默认的构造器来生成Contents。
2. 下面代码展示了基类需要一个有参数的构造器
1 public class Wrapping 2 { 3 private int i; 4 public Wrapping(int x) { i = x; } 5 public int value() { return i; } 6 } 7 // 8 public class Parcel8 9 {10 public Wrapping wrapping(int x)11 {12 return new Wrapping(x)13 {14 public int value()15 { 16 return super.value() * 47; 17 }18 };19 }20 public static void main(String[] args)21 {22 Parcel8 p = new Parcel8();23 Contents w = p.wrapping();24 }25 }
在该例子中,可以看到Wrapping拥有一个要求传递参数的构造器。
这里是将x传进new Warpping(x)。
在匿名内部类中,只需要简单地传递合适的参数给基类的构造器即可。
3. 在匿名内部类定义中,对其进行初始化操作
1 public class Parcel9 2 { 3 public Destination destination(final String dest) 4 { 5 return new Destination() 6 { 7 private String lable = dest; 8 public String readLable() { return lable; } 9 };10 }11 public static void main(String[] args)12 {13 Parcel9 p = new Parcel9();14 Destination d = p.destination("Tasmaina");15 }16 }
在new Destination()中,使用到了在其外部定义的变量dest,编译器要求我们引用的参数是final的
即: 如果定义一个匿名内部类,并且希望他使用一个在其外部定义的对象,那么编译器会要求其参数引用final的。
4.
如果只是简单的给一个字段赋值,那么3例中的方法是很好的。但是,如果想做一个类似构造器的行为,但是匿名类中不可能有命名构造器(其原因是因为它根本就没有名字)。通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。具体实例如下:
1 abstract class Base 2 { 3 public Base(int i) 4 { 5 System.out.println("Base coustructor. i = " + i); 6 } 7 public abstract void f(); 8 } 9 public class AnonymousConstructor10 {11 public static Base getBase(int i)12 {13 return new Base(i)14 {15 { System.out.println("Inside instance initializer "); }16 public void f()17 {18 System.out.println("In anonymous f()");19 }20 };21 }22 public static void main(String[] args)23 {24 Base base = getBase(47);25 base.f();26 }27 }
注意: 在此例中,并没有要求变量 i 一定是final的,因为 i 被传递给匿名类的基类的构造器,并没有在匿名类内部被直接使用。
5. 下面代码为带实例初始化的形式
1 public class Parcel10 2 { 3 public Destination destination(final String dest, final float price) 4 { 5 return new Destination() 6 { 7 private int cost; 8 { 9 cost = Math.round(price);10 if(cost > 100)11 System.out.println("Over budget");12 }13 private String lable = dest;14 public String readLable() { return lable; }15 };16 }17 public static void main(String[] args)18 {19 Parcel10 p = new Parcel10();20 Destination d = p.destination("Tasmaina", 101.395F);21 }22 }
其中 destination 的参数必须是 final 的,因为他们在匿名类中被使用到了。
在实例初始化的内部,有 if 语句,它们不能作为实例初始化动作的一部分。所以对于匿名类而言,实例初始化的实际效果就是构造器。(但是,你不能重载实例初始化方法,所以仅有一个这样的构造器)
匿名内部类与正规继承相比是受到限制的,因为匿名内部类既可以扩展类,也可以实现接口,但是不能二者兼备。并且,如果是实现接口,也只能实现一个接口。