匿名内部类
一、 内部类特性
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.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的引用。(向上转型内容参照:https://www.cnblogs.com/Leo-Xia/p/10917614.html)
关于分号的说明:在匿名内部类末尾的分号,并不是用来标记此内部类结束的。实际上,它标记的是表达式的结束,只不过这个表达式正巧包含了匿名内部类,因此,这与别的地方使用的分号是一致的。
上述匿名内部类的代码,是下面代码的简化版本:
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 AnonymousConstructor 10 { 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 语句,它们不能作为实例初始化动作的一部分。所以对于匿名类而言,实例初始化的实际效果就是构造器。(但是,你不能重载实例初始化方法,所以仅有一个这样的构造器)
匿名内部类与正规继承相比是受到限制的,因为匿名内部类既可以扩展类,也可以实现接口,但是不能二者兼备。并且,如果是实现接口,也只能实现一个接口。