匿名内部类的创建
话不多说,先看看下面这段奇怪的代码:
public class OuterClass {
/**
* 获取 Contents 对象
* @return
*/
public Contents getContents() {
return new Contents() {
private int i = 10;
@Override
public int value() {
return i;
}
};
}
}
class Test {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
//创建 contents 对象
Contents contents = outerClass.getContents();
System.out.println(contents.value());
}
}
其中的 Contents 是一个接口:
public interface Contents {
int value();
}
在这个例子的 getContents() 方法里面,返回值的生成和表示这个返回值的类结合在了一起,而且这个类没有名字,这就是匿名内部类了。匿名内部类需要创建一个继承自一个父类或实现一个接口的匿名类的对象。通过 new 关键字来将这个对象向上转型为父类或者接口的引用。
注意事项
-
我们知道接口或者抽象类是不能通过 new 关键字来创建对象的,必须通过它们的实现类来 new
出它们的实例,所以在上例中直接使用匿名内部类来创建一个 Contents
实例,由于匿名内部类不能是抽象类,所以必须要实现抽象父类或者接口中的抽象方法。 -
在匿名内部类的末尾有一个分号,这个分号不是用来标记匿名内部类结束的,它标记的是方法内的表达式的结束,只是这个表达式包含了匿名内部类。
-
匿名内部类因为没有名字,所以在匿名内部类内部是不可能存在构造器的,同时也不存在静态的成员变量或者静态方法。
匿名内部类只能访问 final 局部变量
如果定义了一个匿名内部类,而且需要在内部类当中使用一个在局部变量,那么编译器就会要求这个变量是 final 的。但是在 java8 中就不需要显示去指定变量是 final 的了,因为它会自动将其声明为 final 类型,这称为 effectively final,只要匿名内部类去访问一个局部变量,那么这个变量无论是否被 final 修饰,它都会被自动声明为 final 的,并且不允许再被修改。
public class OuterClass {
/**
* 获取 Contents 对象
*
* @return
*/
public Contents getContents(int n) {
//局部变量 i
int i = n;
return new Contents() {
@Override
public int value() {
//该操作不允许
//n = 5;
//i = 5;
return i;
}
};
}
}
class Test {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
//创建 contents 对象
Contents contents = outerClass.getContents(10);
System.out.println(contents.value());
}
}
代码中,方法 getContent() 的参数 n 和方法内部定义的变量 i,都被自动声明为 final 的,如果想对这两个变量重新赋值是不被允许的。
为什么匿名内部类只能调用 final 的局部变量呢?因为内部类并不是调用外面的局部变量,而是通过自己的构造器对传入的变量进行拷贝,然后再调用这个拷贝后的变量。如果在匿名内部类内部改变这个变量的值,那么就会造成数据不一致,所以需要通过 final 来让变量值不可变。
匿名内部类的使用
这里我们引用一下文章 工厂模式 中的代码,使用匿名内部类来获取工厂对象,代码改动如下:
/**
* 水果接口
* Created by FM on 2019/5/8.
*/
public interface Fruit {
//定义两个抽象方法
void buyFruit();
void eatFruit();
}
/**
* 工厂接口
*/
interface FruitFactory {
//获取水果抽象方法
Fruit getFruit();
}
/**
* 香蕉实现水果接口
*/
class Banana implements Fruit {
private Banana() {
}
@Override
public void buyFruit() {
System.out.println("buy banana!");
}
@Override
public void eatFruit() {
System.out.println("eat banana!");
}
/**
* 通过匿名内部类创建工厂对象
*/
public static FruitFactory factory = new FruitFactory() {
@Override
public Fruit getFruit() {
return new Banana();
}
};
}
/**
* 梨实现水果接口
*/
class Pear implements Fruit {
private Pear() {
}
@Override
public void buyFruit() {
System.out.println("buy pear!");
}
@Override
public void eatFruit() {
System.out.println("eat pear!");
}
/**
* 通过匿名内部类创建工厂对象
*/
public static FruitFactory factory = new FruitFactory() {
@Override
public Fruit getFruit() {
return new Pear();
}
};
}
class FactoryTest{
/**
* 消费方法,传入工厂对象
* @param factory
*/
public static void consume(FruitFactory factory){
//由工厂对象得到水果对象
Fruit fruit = factory.getFruit();
//买水果,吃水果
fruit.buyFruit();
fruit.eatFruit();
}
public static void main(String[] args) {
//调用消费方法
consume(Banana.factory);
consume(Pear.factory);
}
}
我们在水果的具体实现类 Banana 和 Pear 中使用了匿名内部类来获取工厂 FruitFactory 的对象,这个对象是静态的,这样就可以直接通过类名直接获取对应的工厂对象了;同时这两个实现类的构造器也不需要对外开放了,可以改为 private 的。