关于 Java 匿名类你需要知道的 Posted by xmpace on 2019-10-17

上一篇文章介绍了一种利用匿名类的实例初始化块的初始化方法,在那篇文章里主要讲的是实例初始化块的用法,这篇文章再补补匿名类的课。

匿名类大家经常写,但估计匿名类的语法大家从来没研究过。

常见的匿名类示例如下:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
};

匿名类的定义是一个表达式,匿名类表达式的语法跟调用构造函数差不多,只是多了一块类定义的代码。

匿名类表达式语法由下面部分组成:

  1. new 操作符
  2. 要实现的接口名或要继承的类名,在上面的例子里,匿名类实现了接口 Runnable
  3. 后面是一对括号,括号里面是调用构造器的参数,就像构造普通类实例一样。如果是实现接口,接口是没有构造器的,所以括号里为空。
  4. 类声明体,在类声明体中,可以声明方法,但是不能写语句

匿名类是没办法为其声明构造函数的,因为匿名类没有类名。

继续把上一篇文章的例子搬出来分析吧:

Map<String, String> map = new HashMap<String, String>() {
    {
        put("keyA", "valueA");
        put("keyB", "valueB");
    }
};

这个例子中,map 是一个匿名类的实例,该匿名类继承自 HashMap,调用 HashMap 的无参构造函数,匿名类的类体中有一个实例初始化块的代码块。

因此,这段代码首先会调用基类 HashMap 的构造函数,然后调用实例初始化块。

关于匿名类有个很经典的问题,即变量捕获的问题,也就是匿名类引用的外部类的变量,必须声明为 final(1.8以后不必声明为 final,但其实仍要求是等效于 final 的,也就是语法糖而已)。

其实并非匿名类才有这个问题,本地类一样有该问题,只不过本地类没有匿名类用得多,所以大家一般都讨论匿名类的 final 问题。

至于为什么引用的外部类变量必须是 final,这个留到下篇文章详细写一下。