在Java中匿名类有什么用?我们可以说使用匿名类是Java的优点之一吗?


当前回答

我有时使用它们作为Map实例化的语法hack:

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

它在执行大量put语句时节省了一些冗余。然而,当外部类需要通过远程序列化时,我也遇到了这样做的问题。

其他回答

是的,匿名内部类绝对是Java的优势之一。

使用匿名内部类,您可以访问周围类的final变量和成员变量,这在侦听器等方面很方便。

但是一个主要的优点是,内部类代码(至少应该)与周围的类/方法/块紧密耦合,具有特定的上下文(周围的类、方法和块)。

所谓的“匿名类”,我想你是指匿名的内部类。

匿名内部类在创建具有某些“额外”(如重写方法)的对象实例时非常有用,而不必实际继承一个类。

我倾向于使用它作为附加事件监听器的快捷方式:

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

使用这个方法可以使编码更快一点,因为我不需要创建一个额外的实现ActionListener的类——我可以实例化一个匿名的内部类,而不需要实际创建一个单独的类。

我只在“快速而肮脏”的任务中使用这种技巧,让整个类感觉没有必要。拥有多个做完全相同事情的匿名内部类应该被重构为一个实际的类,无论是内部类还是单独的类。

内部类与外部类的实例相关联,有两种特殊类型:局部类和匿名类。匿名类使我们能够同时声明和实例化一个类,从而使代码简洁。当我们只需要一个本地类一次时,我们使用它们,因为它们没有名字。

考虑一下文档中的例子,我们有一个Person类:

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

我们有一个方法来打印匹配搜索条件的成员:

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

其中CheckPerson是一个像这样的接口:

interface CheckPerson {
    boolean test(Person p);
}

现在我们可以使用匿名类来实现这个接口来指定搜索条件:

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

这里的接口非常简单,而且匿名类的语法看起来笨拙而不清楚。

Java 8引入了一个术语函数接口,它是一个只有一个抽象方法的接口,因此我们可以说CheckPerson是一个函数接口。我们可以使用Lambda表达式,它允许我们将函数作为方法参数传递给:

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

我们可以使用标准功能接口Predicate来代替接口CheckPerson,这将进一步减少所需的代码量。

似乎这里没有人提到,但你也可以使用匿名类来保存泛型类型参数(通常由于类型擦除而丢失):

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

如果你用匿名的方式实例化这个类

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

然后,这样的holder实例将包含传递类型的非擦除定义。

使用

这对于构建验证器/反序列化器非常方便。此外,您还可以使用反射实例化泛型类型(因此,如果您想在参数化类型中执行新的T()—欢迎您!)

不便/限制

您应该显式地传递泛型参数。如果不这样做,将导致类型参数丢失 每次实例化都会花费编译器生成额外的类,这会导致类路径污染/jar膨胀

我有时使用它们作为Map实例化的语法hack:

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

它在执行大量put语句时节省了一些冗余。然而,当外部类需要通过远程序列化时,我也遇到了这样做的问题。