我知道匿名类在实现监听器和类似的东西时可以节省打字。它们试图替代闭包的某些用法。
但是社区如何看待这种语言特性的价值?它有意义吗?您是否经常使用它?它是否使代码更清晰、更易于理解和更易于维护?还是匿名类会降低代码的可读性?
您的意见是什么,请提供方便的示例/论据来支持您的意见?
我知道匿名类在实现监听器和类似的东西时可以节省打字。它们试图替代闭包的某些用法。
但是社区如何看待这种语言特性的价值?它有意义吗?您是否经常使用它?它是否使代码更清晰、更易于理解和更易于维护?还是匿名类会降低代码的可读性?
您的意见是什么,请提供方便的示例/论据来支持您的意见?
在不需要为了执行某些任务而拥有一个成熟的类的情况下,我倾向于使用匿名内部类。例如,如果我想实现ActionListener
or Runnable
,但我认为没有必要使用内部类。例如,对于启动一个简单的Thread
,使用匿名内部类可能更具可读性:
public void someMethod()
{
new Thread(new Runnable() {
public void run()
{
// do stuff
}
}).start();
}
在某些情况下,例如上面的示例,它可以增加可读性,特别是对于一次性任务,因为要执行的代码都写在一个地方。使用内部类会“离域”代码:
public void someMethod()
{
new Thread(new MyRunnable()).start();
}
// ... several methods down ... //
class MyRunnable implements Runnable
{
public void run()
{
// do stuff
}
}
尽管如此,如果要重复相同的事情,它确实应该是一个单独的类,无论是普通类还是内部类。
我倾向于在我只是尝试的程序中使用匿名内部类,而不是将其作为实际应用程序的核心功能。
匿名内部类的另一个好用处是当您需要初始化像 ArrayList 和 Set 这样的集合时。这种做法也称为双括号初始化 ,例如,
private static final Set<String> VALID_CODES = new HashSet<String>() {{
add("XZ13s");
add("AB21/X");
add("YYLEX");
add("AR2D");
}};
显然,这不仅限于收藏;它可用于初始化任何类型的对象——例如 Gui 对象:
add(new JPanel() {{
setLayout(...);
setBorder(...);
add(new JLabel(...));
add(new JSpinner(...));
}});
我的观点是匿名类使代码的可读性降低。对于实现侦听器,匿名类很有用。对于开发GWT应用程序,匿名类是更好的选择。对于这些情况,如果我们不使用匿名类,那么代码行数将会增加。
我们定期使用匿名类。我发现它们很容易用于实现只有一两个方法的接口,并且在其他任何地方都没有使用该功能。如果您在其他地方再次使用相同的功能,则应该有一个真正的类可以重用。
使用匿名类是提高还是降低易读性是一个品味问题。主要问题绝对不在这里。
匿名类,就像内部类一样,携带对封闭类的引用,从而使没有它的东西成为非私有的东西。简而言之,封闭类的this引用可能会通过内部类逃逸。所以答案是:如果内部类自己发布,那么使用它是一种非常糟糕的做法,因为这会自动发布封闭类。例如:
changeManager.register(new ChangeListener() {
public void onChange(...) {
...
}});
在这里,匿名ChangeLsener被传递给ChangeManager的register方法。这样做也会自动发布封闭类。
这绝对是一个不好的做法。
我主要将匿名类用于只有一个方法的接口,即Runnable或ActionListener。大多数较大的接口在已经存在的类中保留自己的类或实现。因为这是我的观点,我不需要论据来支持它。
如果尽可能限制范围和访问是一件好事,那么匿名类就非常好。它们的范围仅限于需要它们的一类。在适当的时候,我会说匿名课程很好。
一旦你复制相同的功能,它就变成了一个坏主意。将其重构为一个独立的公共类。具有重构功能的 IDE 使这变得容易。
匿名类主要出现在 GUI 应用程序中,专门用于事件处理。匿名类在实现包含一个或两个方法的小接口的情况下很有用..例如..您有一个有两个或三个线程的类,并且您想要使用这些线程执行两个或三个不同的任务。在这种情况下,您可以借助匿名类来执行您想要的任务。看下面的例子
class AnonymousClass{
public static void main(String args[]){
Runnable run1=new Runnable(){
public void run(){
System.out.println("from run1");
}
};
Runnable run2=new Runnable(){
public void run(){
System.out.println("from run2");
}
};
Runnable run3=new Runnable(){
public void run(){
System.out.println("from run3");
}
};
Thread t1=new Thread(run1);
Thread t2=new Thread(run2);
Thread t3=new Thread(run3);
t1.run();t2.run();t3.run();
}
}
输出:
从运行1
从 run2
从 run3
在上面的代码片段中,我使用了三个线程来执行三个不同的任务。看,我创建了三个匿名类,其中包含 run 方法的实现,以执行三个不同的小任务。
使用它们是有道理的,但你必须知道下面正在做什么。只有当我需要一门课来做一些我在其他地方不需要的非常具体的事情时,我才会使用它们。
这取决于您将它们与什么进行比较。我宁愿拥有它们也不愿没有它们,但是我宁愿能够为 Arrays.sort() 之类的方法提供纯代码块,而不必显式创建一个包含我的 compare() 实现的类。
匿名类在本质上没有什么不同或特别之处。它们最终只是支持引用外部类的语法糖。这使得编写适配器更容易——就像 Collections 框架返回的大多数迭代器实现一样。
我主要使用匿名类 a) 如果接口有一个或两个方法并且它不会影响可读性的简写符号
b) 我无法证明创建新类的情况,例如在摇摆中,当您必须附加一个 actionlistner 以让 JButton 进行一些琐碎的操作时。
我同意许多其他人所说的,即它们仅在使用一次时对小型接口很有用。但我还要添加一个限制,即如果必须更改匿名类外部的代码才能使其工作,则不要使用匿名类。
如果您必须开始将变量声明为 final 以适应 anon 类,因为它引用了它们,那么请改用内部类。我还看到了一些不良代码气味,其中最终数组(大小为 1)用于从匿名类返回结果。
匿名类不会“隐藏”代码,但它们确实倾向于使其可重用性稍差。请注意,这也适用于闭包。
在某些方面,它们允许进行一些不错的重构,因为您可以将代码传递给方法。这可以非常有效地用于减少重复,我当然不反对匿名类/闭包,但是在某些情况下它们可能是一个缺点。
首先考虑您传入的匿名内部类代码不适合在您的代码中重用。如果您在其他代码中执行相同的操作,则必须将其重写为匿名内部类以外的其他内容才能重用它,此时甚至可能很难知道其他地方有代码可重用。
除了缺乏重用之外,参数化也很困难,这导致了我最大的抱怨……它们往往会导致复制和粘贴代码。
我见过不少 GUI,其中有人从匿名内部类开始作为事件响应者。许多人不得不做一些稍微不同的事情,例如,5行代码,唯一的区别是中间的一个字符串。一旦您习惯了使用内部类,简单的解决方案就是复制并粘贴块并替换该字符串。
创建具有字符串参数的新“命名”类并将该类传递给所有方法的解决方案很少发生在那个时候。这个命名类可以使用参数或继承来定义不同的行为以及代码。
我是闭包的粉丝,并不讨厌匿名类——只是指出我见过的一些陷阱。
因此,借助接口中方法的 java 8 新特性static
,您可以使用匿名内部类来返回接口的实例。例如:
interface Person {
String getName();
int getAge();
static Person newInstance() {
return new Person() {
public String getName() {
return "Bob";
}
public int getAge() {
return 99;
}
}
}
}
你可以得到一个像这样的实例:
Person person = Person.newInstance();
基本上,这允许您的接口的单个默认实现。