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


当前回答

匿名内部类在为不同的对象提供不同的实现时是有益的。但是应该非常谨慎地使用,因为它会给程序的可读性带来问题。

其他回答

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

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膨胀

匿名内部类实际上是闭包,所以它们可以用来模拟lambda表达式或“委托”。以这个接口为例:

public interface F<A, B> {
   B f(A a);
}

您可以匿名使用它在Java中创建一级函数。假设你有以下方法,返回给定列表中第一个比i大的数字,如果没有比i大的数字,则返回i:

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

然后你有另一个方法返回给定列表中第一个比i小的数字,如果没有比i小的数字,则返回i:

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

这些方法几乎是相同的。使用一类函数类型F,我们可以将它们重写为一个方法,如下所示:

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

你可以使用匿名类来使用firstMatch方法:

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

这是一个非常做作的例子,但是很容易看出,能够像传递值一样传递函数是一个非常有用的特性。请看Joel自己写的“你的编程语言能做到这一点吗?”

用这种风格编写Java的一个很好的库:函数式Java。

您可以这样使用匿名类

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

匿名内部类用于创建一个永远不会被再次引用的对象。它没有名称,在同一个语句中声明和创建。 这用于通常使用对象变量的地方。用new关键字替换变量,调用构造函数和{and}内的类定义。

当用Java编写线程程序时,它通常是这样的

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

这里使用的ThreadClass是用户定义的。该类将实现创建线程所需的Runnable接口。在ThreadClass中run()方法(Runnable中唯一的方法)也需要实现。 显然,去掉ThreadClass会更有效,这正是匿名内部类存在的原因。

请看下面的代码

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

这段代码替换了最上面的示例中对任务的引用。Thread()构造函数中的匿名内部类并没有单独的类,而是返回一个未命名的对象,该对象实现了Runnable接口并重写了run()方法。方法run()将包含执行线程所需工作的语句。

在回答“匿名内部类是否是Java的优点之一”这个问题时,我不得不说我不太确定,因为目前我对许多编程语言都不熟悉。但我能说的是,这绝对是一种更快更简单的编码方法。

参考资料:山姆自学Java在21天第七版

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

考虑一下文档中的例子,我们有一个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,这将进一步减少所需的代码量。