317

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

4

18 回答 18

378

通过“匿名类”,我认为您的意思是匿名内部类

匿名内部类可以在创建具有某些“附加”(例如覆盖方法)的对象实例时很有用,而不必实际子类化一个类。

我倾向于将其用作附加事件侦听器的快捷方式:

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

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

我只将这种技术用于“快速而肮脏”的任务,让整个班级感觉没有必要。拥有多个做同样事情的匿名内部类应该重构为一个实际的类,无论是内部类还是单独的类。

于 2008-12-10T05:50:57.967 回答
76

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

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

您可以匿名使用它在 Java中创建一流的函数。假设您有以下方法,它返回给定列表中大于 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:

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 的一个不错的库:Functional Java。

于 2008-12-10T07:54:45.347 回答
57

匿名内部类用于以下场景:

1.) 对于覆盖(子类化),当类定义除当前情况外不可用时:

class A{
    public void methodA() {
        System.out.println("methodA");
    }
}

class B{
    A a = new A() {
        public void methodA() {
            System.out.println("anonymous methodA");
        }
    };
}

2.) 对于实现接口,当仅当前情况需要实现接口时:

interface InterfaceA{
    public void methodA();
}

class B{
    InterfaceA a = new InterfaceA() {
        public void methodA() {
            System.out.println("anonymous methodA implementer");
        }
    };
}

3.)参数定义的匿名内部类:

interface Foo {
    void methodFoo();
}

class B{
    void do(Foo f) { }
}

class A{
    void methodA() {
        B b = new B();
        b.do(new Foo() {
            public void methodFoo() {
                System.out.println("methodFoo");
            } 
        });
    } 
} 
于 2014-03-09T18:42:28.283 回答
48

我有时将它们用作 Map 实例化的语法技巧:

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

对比

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

它在执行大量 put 语句时节省了一些冗余。但是,当外部类需要通过远程处理进行序列化时,我也遇到了问题。

于 2008-12-11T20:59:36.867 回答
19

它们通常用作回调的详细形式。

我想你可以说它们与没有它们相比是一个优势,并且每次都必须创建一个命名类,但是类似的概念在其他语言中实现得更好(作为闭包或块)

这是一个摆动的例子

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

虽然它仍然很冗长,但它比强迫你为每个这样的丢弃监听器定义一个命名类要好得多(尽管取决于情况和重用,这可能仍然是更好的方法)

于 2008-12-10T05:51:13.583 回答
9

您可以在需要在另一个函数中为特定目的创建类的情况下使用它,例如,作为侦听器、作为可运行对象(生成线程)等。

这个想法是您从函数代码内部调用它们,因此您永远不会在其他地方引用它们,因此您不需要命名它们。编译器只是枚举它们。

它们本质上是语法糖,随着它们变大,通常应该移到别处。

我不确定这是否是 Java 的优势之一,但如果您确实使用它们(不幸的是,我们都经常使用它们),那么您可能会争辩说它们是其中之一。

于 2008-12-10T05:50:04.520 回答
6

匿名类指南。

  1. 匿名类同时声明和初始化。

  2. 匿名类必须扩展或实现一个且只有一个类或接口。

  3. 由于匿名类没有名字,所以只能使用一次。

例如:

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});
于 2012-05-23T17:20:05.667 回答
5

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

使用匿名内部类,您可以访问周围类的最终变量和成员变量,这在侦听器等中派上用场。

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

于 2008-12-10T07:50:45.667 回答
5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

这也是使用线程的匿名内部类型的示例之一

于 2013-03-21T05:59:11.083 回答
4

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

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

于 2016-11-17T09:04:30.787 回答
3

我使用匿名对象来调用新线程..

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();
于 2013-10-10T05:17:27.783 回答
2

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

于 2013-12-17T06:59:06.983 回答
1

匿名类在类终结中的主要用法之一,称为终结器守护者。在 Java 世界中,应该避免使用 finalize 方法,直到你真正需要它们。您必须记住,当您覆盖子类的 finalize 方法时,您也应该始终调用super.finalize(),因为超类的 finalize 方法不会自动调用,并且您可能会遇到内存泄漏的问题。

所以考虑到上面提到的事实,你可以使用匿名类,比如:

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

使用这种技术,您可以减轻自己和其他开发人员调用which need finalize 方法super.finalize()的每个子类的负担。HeavyClass

于 2012-12-28T10:38:18.650 回答
1

您可以通过这种方式使用匿名类

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});
于 2015-07-03T13:23:56.413 回答
1

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

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的实例将包含传递类型的非擦除定义。

用法

这对于构建验证器/反序列化器非常方便。你也可以用反射实例化泛型类型(所以如果你想new T()在参数化类型中做 - 欢迎你!)

缺点/限制

  1. 您应该显式传递泛型参数。不这样做会导致类型参数丢失
  2. 每个实例化都会花费你额外的类来由编译器生成,这会导致类路径污染/jar 膨胀
于 2016-08-17T20:06:50.523 回答
1

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

在 Java 中编写线程程序时,它通常看起来像这样

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

此处使用的ThreadClass将是用户定义的。此类将实现Runnable创建线程所需的接口。在方法ThreadClassrun()(仅方法 in Runnable)也需要实现。很明显,摆脱ThreadClass会更有效,这正是存在匿名内部类的原因。

看下面的代码

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

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

在回答匿名内部类是否是 Java 的优势之一的问题时,我不得不说我不太确定,因为我目前还不熟悉许多编程语言。但我能说的是,它绝对是一种更快、更容易的编码方法。

参考资料:Sams 21 天自学 Java 第七版

于 2018-03-21T12:24:07.270 回答
0

优化代码的最佳方式。此外,我们可以用于类或接口的覆盖方法。

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}
于 2017-08-14T14:18:00.770 回答
0

另一个优点:
如您所知,Java 不支持多重继承,因此如果您使用“线程”类作为匿名类,那么该类仍然有一个空间供任何其他类扩展。

于 2018-05-12T12:22:03.323 回答