如何在Java中使用匿名内部类?

时间:2008-12-10 05:42:07

标签: java anonymous-class anonymous-inner-class

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

18 个答案:

答案 0 :(得分:354)

通过“匿名课程”,我认为你的意思是anonymous inner class

在创建具有某些“额外”的对象实例(例如重写方法)时,匿名内部类可以变得有用,而不必实际为子类创建子类。

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

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

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

我只使用这种技术进行“快速而肮脏”的任务,使整个班级感觉不必要。有多个完全相同的匿名内部类应该重构为实际的类,无论是内部类还是单独的类。

答案 1 :(得分:73)

匿名内部类是有效的闭包,因此可以用它们来模拟lambda表达式或“委托”。例如,使用此界面:

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

您可以匿名使用此方法在Java中创建first-class function。假设您有以下方法返回给定列表中大于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);

这是一个非常人为的例子,但很容易看出能够传递函数就像它们是值一样是一个非常有用的功能。见乔尔本人的"Can Your Programming Language Do This"

一个很好的用于以这种方式编写Java的库:Functional Java.

答案 2 :(得分:51)

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

1.)对于Overriding(子类),当除了当前情况之外的类定义不可用时:

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");
       } 
     });
   } 
 } 

答案 3 :(得分:45)

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

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

VS

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

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

答案 4 :(得分:18)

它们通常用作冗长的回调形式。

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

这是一个摇摆的例子

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

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

答案 5 :(得分:8)

你需要在另一个函数内为特定目的创建一个类,例如作为一个监听器,一个runnable(产生一个线程)等等。

这个想法是你从一个函数的代码中调用它们,所以你永远不会在别处引用它们,所以你不需要命名它们。编译器只是枚举它们。

它们基本上是语法糖,一般应该随着它们变大而转移到别处。

我不确定它是否是Java的优势之一,但如果你确实使用它们(不幸的是我们经常使用它们),那么你可以说它们是一体的。

答案 6 :(得分:6)

匿名类的GuideLines。

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

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

  3. 由于anonymouse类没有名称,因此只能使用一次。

  4. 例如:

    button.addActionListener(new ActionListener(){
    
                public void actionPerformed(ActionEvent arg0) {
            // TODO Auto-generated method stub
    
        }
    });
    

答案 7 :(得分:5)

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

使用匿名内部类,您可以访问周围类的final和member变量,并且在侦听器中派上用场等。

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

答案 8 :(得分: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();

这也是使用线程

的匿名内部类型的示例之一

答案 9 :(得分:4)

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

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

答案 10 :(得分:3)

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

考虑来自doc的示例,其中我们有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 引入了一个术语Functional Interface,这是一个只有一个抽象方法的接口,因此我们可以说CheckPerson是一个功能接口。我们可以使用Lambda Expression,它允许我们将函数作为方法参数传递:

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

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

答案 11 :(得分:2)

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

答案 12 :(得分:1)

在类完成中匿名类的一个主要用法,它叫做终结器监护人。在Java世界中,应该避免使用finalize方法,直到您真正需要它们为止。你必须记住,当你覆盖子类的finalize方法时,你也应该总是调用super.finalize(),因为超类的finalize方法不会自动调用,你可能会遇到内存泄漏问题。 / p>

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

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

使用这种技术,您可以放心自己和其他开发人员在super.finalize()的每个需要finalize方法的子类上调用HeavyClass

答案 13 :(得分:1)

您可以这样使用匿名类

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

答案 14 :(得分: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膨胀

答案 15 :(得分: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();
    }

}

答案 16 :(得分:0)

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

用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();

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

回答关于匿名内部类是否是Java的优势之一的问题,我不得不说我不太确定,因为我目前不熟悉许多编程语言。但我可以说它绝对是一种更快捷,更简单的编码方法。

参考文献:Sams在21天第七版自学Java

答案 17 :(得分:0)

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