Java中匿名类的用途是什么?我们可以说使用匿名类是Java的优势之一吗?
Java中匿名类的用途是什么?我们可以说使用匿名类是Java的优势之一吗?
Answers:
我将“匿名类”理解为匿名内部类。
当使用某些“额外”(例如,覆盖方法)创建对象的实例时,匿名内部类会变得很有用,而不必实际子类化。
我倾向于将它用作附加事件侦听器的快捷方式:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
使用此方法可以使编码更快一些,因为我不需要创建额外的类来实现ActionListener
-我可以实例化一个匿名内部类而无需实际创建单独的类。
我只将这种技术用于“快速而肮脏的”任务,在这些任务中使整个课程变得不必要。具有多个功能完全相同的匿名内部类应重构为实际类,无论是内部类还是单独的类。
overloading methods
和不overriding methods
?
匿名内部类实际上是闭包,因此可以用来模拟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);
这是一个非常人为的示例,但是很容易看出,能够像传递值一样传递函数是一个非常有用的功能。请参见“可以将您的编程语言做到这一点”乔尔自己。
一个很好的以这种风格进行Java编程的库:Functional Java。
在以下情况下使用匿名内部类:
1.)对于Overriding(Sub classing),当除当前情况以外不可用类定义时:
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");
}
});
}
}
我有时将它们用作Map实例化的语法技巧:
Map map = new HashMap() {{
put("key", "value");
}};
与
Map map = new HashMap();
map.put("key", "value");
在执行很多put语句时,它可以节省一些冗余。但是,当外部类需要通过远程处理进行序列化时,这样做也会遇到问题。
它们通常用作详细的回调形式。
我想您可以说,与没有它们和每次都必须创建一个命名类相比,它们是一个优势,但是在其他语言(如闭包或块)中实现类似的概念要好得多
这是一个摇摆的例子
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
尽管它仍然很冗长,但比强迫您为每个这样的抛弃式侦听器定义一个命名类要好得多(尽管取决于情况和重用,这仍然可能是更好的方法)
myButton.addActionListener(e -> { /* do stuff here */})
或myButton.addActionListener(stuff)
会更短。
匿名类准则。
匿名类被同时声明和初始化。
匿名类必须扩展或实现为一个或仅一个类或接口。
由于匿名类没有名称,因此只能使用一次。
例如:
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
。
是的,匿名内部类绝对是Java的优点之一。
使用匿名内部类,您可以访问周围类的final和member变量,这在侦听器等中很方便。
但是一个主要的优点是(至少应该)紧密耦合到周围的类/方法/块的内部类代码具有特定的上下文(周围的类,方法和块)。
一个内部类与外部类的一个实例相关联,并且有两个特殊类型:本地类和匿名类。匿名类使我们能够同时声明和实例化一个类,从而使代码简洁。当我们只需要一次本地类时就使用它们,因为它们没有名称。
考虑来自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引入了术语“ 功能接口”,它是只有一种抽象方法的接口,因此可以说CheckPerson
是功能接口。我们可以使用Lambda表达式,该表达式允许我们将函数作为方法参数传递为:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
我们可以使用标准的功能接口Predicate
代替接口CheckPerson
,这将进一步减少所需的代码量。
类最终定义中匿名类的主要用法之一,称为finalizer Guardian。在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
}
};
}
使用此技术,您和其他开发人员都无需调用需要finalize方法的super.finalize()
每个子类HeavyClass
。
似乎这里没有人提及,但是您也可以使用匿名类来保存泛型类型参数(通常由于类型擦除而丢失):
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()
在参数化类型中进行操作,欢迎您!)。
优化代码的最佳方法。同样,我们可以将其用于类或接口的重写方法。
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();
}
}
一个匿名内部类是用于创建再也不会被引用的对象。它没有名称,并且在同一条语句中声明和创建。在通常使用对象变量的地方使用它。您用替换变量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()
构造函数内部的Anonymous Inner Class不需要单独的类,而是返回一个未命名的对象,该对象实现了Runnable
接口并覆盖了run()
方法。该方法run()
将在内部包含执行线程所需工作的语句。
在回答有关匿名内部类是否是Java的优点之一的问题时,我不得不说我不确定,因为我目前不熟悉许多编程语言。但是我可以说的是,这绝对是一种更快,更轻松的编码方法。
参考:Sams在21天中自学Java第七版
另一个优点:
您知道Java不支持多重继承,因此,如果您将“ Thread”类用作匿名类,则该类还剩下一个空间可以扩展其他任何类。