Java中如何使用匿名(内部)类?
Java中匿名类的用法是什么? 我们可以说使用匿名类是Java的好处之一吗?
通过一个“匿名class”,我认为你的意思是匿名的内部class 。
一个匿名的内部类可以在用一些特殊的“额外”(比如重载方法)来创build一个对象的实例时,而不必实际上inheritance一个类。
我倾向于将其用作附加事件侦听器的快捷方式:
button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // do something } });
使用这种方法使编码更快一点,因为我不需要额外的类来实现ActionListener
– 我可以实例化一个匿名的内部类而不需要实际创build一个单独的类。
我只用这个技巧来做“快速和肮脏”的任务,让整个class级都觉得没有必要。 拥有多个完全相同的匿名内部类应该被重构为一个实际的类,无论它是一个内部类或一个单独的类。
匿名内部类是有效的闭包,所以它们可以用来模拟lambdaexpression式或“委托”。 例如,采取这个接口:
public interface F<A, B> { B f(A a); }
您可以使用这个匿名来创buildJava中的一streamfunction 。 假设您有以下方法返回给定列表中比我大的第一个数字,或者如果没有数字较大,
public static int larger(final List<Integer> ns, final int i) { for (Integer n : ns) if (n > i) return n; return i; }
然后你有另一种方法返回给定列表中的第一个小于我的数字,或者如果没有数字小于我:
public static int smaller(final List<Integer> ns, final int i) { for (Integer n : ns) if (n < i) return n; return i; }
这些方法几乎完全相同。 使用第一类函数typesF,我们可以将它们重写成一个方法如下:
public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) { for (T t : ts) if (ff(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);
这是一个非常人为的例子,但是很容易看到,能够将函数作为值来传递是一个非常有用的function。 请参阅Joel自己的“你的编程语言是否能做到这一点” 。
一个用这种风格编程Java的好库: Functional Java。
我有时使用它们作为Map实例化的语法:
Map map = new HashMap() {{ put("key", "value"); }};
VS
Map map = new HashMap(); map.put("key", "value");
在做很多put语句的时候可以节省一些冗余。 但是,当外部类需要通过远程序列化时,我也遇到了这样做的问题。
在以下情况下使用匿名内部类:
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"); } }); } }
它们通常用作callback的详细forms。
我想你可以说,与没有它们相比,它们是一个优势,并且每次都必须创build一个命名类,但是类似的概念在其他语言(如闭包或块)
这是一个摇摆的例子
myButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { // do stuff here... } });
尽pipe它仍然是一团糟的,但是比强迫你为每个抛弃监听器定义一个命名类要好很多(尽pipe取决于情况和重用,这可能仍然是更好的方法)
在需要为另一个函数内部的特定目的创build一个类的情况下,例如,作为一个监听器,作为一个可运行的(产生一个线程),等等。
这个想法是,你从一个函数的代码中调用它们,所以你从不在别处引用它们,所以你不需要给它们命名。 编译器只是枚举它们。
它们本质上是句法糖,随着它们越来越大,通常应该移到其他地方。
我不确定这是否是Java的好处之一,但如果你真的使用它们(而且我们都经常使用它们,不幸的是),那么你可能会认为它们是一个。
为匿名类指南。
-
匿名类是同时声明和初始化的。
-
匿名类必须扩展或实现为一个且仅有一个类或接口resp。
-
由于anonymouse类没有名字,所以只能使用一次。
例如:
button.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { // TODO Auto-generated method stub } });
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();
这也是使用线程的匿名内部types的例子之一
是的,匿名内部类绝对是Java的优点之一。
通过匿名内部类,您可以访问周围类的最终成员variables和成员variables,并且在监听器等方面很有用。
但是一个主要的优点是内部类代码(至less应该)紧密耦合到周围的类/方法/块,具有特定的上下文(周围的类,方法和块)。
我使用匿名对象来调用新线程..
new Thread(new Runnable() { public void run() { // you code } }).start();
匿名内部类可以是有益的,同时为不同的对象提供不同的实现。 但是应该非常谨慎地使用,因为它会给程序的可读性带来问题。
内部类与外部类的一个实例相关联,并且有两种特殊types:Local类和Anonymous类 。 一个匿名类使我们能够同时声明和实例化一个类,从而使代码更加简洁。 当我们只需要一个本地类时,我们就使用它们,因为它们没有名字。
考虑一下我们有一个Person
类的doc例子:
public class Person { public enum Sex { MALE, FEMALE } String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { // ... } public void printPerson() { // ... } }
我们有一个打印符合search条件的成员的方法:
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); }
现在我们可以使用实现这个接口的匿名类来指定search条件:
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
是一个function接口。 我们可以使用Lambdaexpression式 ,它允许我们将方法parameter passing为:
printPersons( roster, (Person p) -> p.getGender() == Person.Sex.MALE && p.getAge() >= 18 && p.getAge() <= 25 );
我们可以使用一个标准的函数接口Predicate
来代替接口CheckPerson
,这将进一步减less所需的代码量。
匿名类的主要用法之一,在类定型中称为终结者监护人 。 在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 } }; }
使用这种技术,您可以放心,您和其他开发人员在需要敲定方法的HeavyClass
每个子类上调用super.finalize()
。
你可以用这种方式使用匿名类
TreeSet treeSetObj = new TreeSet(new Comparator() { public int compare(String i1,String i2) { return i2.compareTo(i1); } });
似乎没有人在这里提到,但你也可以使用匿名类来保存通用types参数(通常由于types擦除丢失) :
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
实例将包含传递types的非删除定义。
用法
这对于构buildvalidation器/反序列化器非常方便。 你也可以使用reflection实例化generics(所以如果你曾经想在参数化types中做new T()
,那么欢迎你!) 。
缺点/限制
- 你应该明确地传递generics参数。 如果不这样做将导致types参数丢失
- 每个实例都会花费你编译器生成的额外的类,导致类path污染/瓶子膨胀
优化代码的最佳方法。 另外,我们可以使用一个类或接口的重写方法。
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(); } }