[英]Anonymous Inner Type of Standard Java Classes in Content Assist? How are they used?
[英]How are Anonymous inner classes used in Java?
Java中匿名类有什么用? 我们可以说匿名类的使用是Java的优点之一吗?
通过“匿名类”,我认为您的意思是匿名内部类。
匿名内部类在使用某些“额外”(例如覆盖方法)创建对象的实例时非常有用,而无需实际对类进行子类化。
我倾向于将其用作附加事件侦听器的快捷方式:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
使用这种方法可以使编码更快一点,因为我不需要创建一个额外的类来实现ActionListener
—— 我可以只实例化一个匿名内部类,而无需实际创建一个单独的类。
我只将这种技术用于“快速而肮脏”的任务,在这种情况下,让整个班级感觉没有必要。 有多个匿名内部类做完全相同的事情应该重构为一个实际的类,无论是内部类还是单独的类。
匿名内部类实际上是闭包,因此它们可用于模拟 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);
这是一个非常人为的例子,但很容易看出,能够将函数当作值来传递是一个非常有用的特性。 请参阅乔尔本人的“你的编程语言能做到这一点吗” 。
以这种风格编写 Java 的一个不错的库: Functional Java。
匿名内部类用于以下场景:
1.) 对于 Overriding(subclassing),当类定义除当前情况外不可用时:
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...
}
});
虽然它仍然很冗长,但比强迫你为每个丢弃的监听器定义一个命名类要好得多(尽管取决于情况和重用,这可能仍然是更好的方法)
您可以在需要在另一个函数内为特定目的创建类的情况下使用它,例如,作为侦听器、作为可运行对象(生成线程)等。
这个想法是你从函数的代码内部调用它们,所以你永远不会在其他地方引用它们,所以你不需要命名它们。 编译器只是枚举它们。
它们本质上是语法糖,随着它们变大,通常应该移到其他地方。
我不确定这是否是 Java 的优势之一,但如果您确实使用它们(不幸的是,我们都经常使用它们),那么您可能会争辩说它们是其中之一。
匿名类指南。
匿名类同时声明和初始化。
匿名类必须扩展或实现为一个且仅一个类或接口。
由于匿名类没有名称,因此只能使用一次。
例如:
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();
这也是使用线程的匿名内部类型的示例之一
是的,匿名内部类绝对是 Java 的优势之一。
使用匿名内部类,您可以访问周围类的最终变量和成员变量,这在侦听器等中派上用场。
但是一个主要的优点是内部类代码(至少应该)与周围的类/方法/块紧密耦合,具有特定的上下文(周围的类、方法和块)。
内部类与外部类的实例相关联,有两种特殊类型:本地类和匿名类。 匿名类使我们能够同时声明和实例化一个类,从而使代码简洁。 当我们只需要一个本地类时,我们会使用它们,因为它们没有名字。
考虑来自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 表达式,它允许我们将函数作为方法参数传递为:
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
我们可以使用标准的函数接口Predicate
代替接口CheckPerson
,这将进一步减少所需的代码量。
我使用匿名对象来调用新线程..
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
在为不同的对象提供不同的实现时,匿名内部类可能是有益的。 但是应该非常谨慎地使用,因为它会给程序可读性带来问题。
匿名类在类终结中的主要用途之一,称为终结器监护人。 在 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 方法的HeavyClass
每个子类调用super.finalize()
的HeavyClass
。
你可以这样使用匿名类
TreeSet treeSetObj = new TreeSet(new Comparator()
{
public int compare(String i1,String i2)
{
return i2.compareTo(i1);
}
});
这里似乎没有人提到,但您也可以使用匿名类来保存泛型类型参数(通常由于类型擦除而丢失) :
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()
- 欢迎使用!) 。
匿名内部类用于创建一个永远不会被再次引用的对象。 它没有名称,在同一个语句中声明和创建。 这用于通常使用对象变量的地方。 您用new
关键字、对构造函数的调用以及{
和}
的类定义替换变量。
用 Java 编写线程程序时,通常看起来像这样
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
ThreadClass
使用的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 自学 Java 21 天第七版
优化代码的最佳方式。 此外,我们可以用于类或接口的覆盖方法。
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();
}
}
还有一个优势:
正如你所知,Java 不支持多重继承,所以如果你使用“Thread”类作为匿名类,那么该类仍然有一个空间可供任何其他类扩展。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.