使用Anonymous类有什么危害?

mtk*_*mtk 36 java anonymous-class

在阅读这个问题的答案时出现了这个问题 - 如何在java中加入两个列表.这个答案给出了解决方案

List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };
Run Code Online (Sandbox Code Playgroud)

阅读评论,用户说它是邪恶和丑陋的,不应该用于生产.

我想知道使用它有什么害处?为什么在生产中使用它是丑陋的,邪恶的还是坏的?


注意:被问到这是一个问题因为,引用的帖子太旧了(2008年),回答者几个月后就离开了.

Nat*_*tix 49

除了已经提到的关于良好编程风格和继承滥用的问题之外,还有一个更微妙的问题 - 内部类和(非静态)匿名类实例充当闭包.这意味着它们保持对封闭类实例的隐式引用.这可以防止垃圾收集,最终导致内存泄漏.

给出一段示例源代码:

public interface Inner {
    void innerAction();
}

public class Outer {

    public void methodInOuter() {}

    private Inner inner = new Inner() {
        public void innerAction() {
            // calling a method outside of scope of this anonymous class
            methodInOuter();  
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在编译时发生的是,编译器为新的匿名子类创建一个类文件,该子类Inner获得一个所谓的合成字段,其中包含对该Outer类实例的引用.生成的字节码大致相当于这样的东西:

public class Outer$1 implements Inner {

    private final Outer outer; // synthetic reference to enclosing instance

    public Outer$1(Outer outer) {
        this.outer = outer;
    }

    public void innerAction() {
        // the method outside of scope is called through the reference to Outer
        outer.methodInOuter();
    }
}
Run Code Online (Sandbox Code Playgroud)

即使对于从未实际访问封闭类的任何方法或字段的匿名类,例如您的问题中的双括号初始化(DBI)列表,也会发生对封闭实例的引用的捕获.

这导致DBI列表保留对封闭实例的引用,只要它存在,从而防止封闭实例被垃圾收集.假设DBI列表恰好在应用程序中存在了很长时间,例如作为MVC模式中模型的一部分,并且捕获的封闭类是例如a JFrame,这是一个包含大量字段的相当大的类.如果您创建了几个DBI列表,则会很快发生内存泄漏.

一种可能的解决方案是仅在静态方法中使用DBI ,因为在其范围内没有可用的封闭实例.

另一方面,我仍然认为在大多数情况下仍然不需要使用DBI.至于列表加入,我会创建一个简单的可重用方法,它不仅更安全,而且更简洁明了.

public static <T> List<T> join(List<? extends T> first, List<? extends T> second) {
    List<T> joined = new ArrayList<>();
    joined.addAll(first);
    joined.addAll(second);
    return joined;
}
Run Code Online (Sandbox Code Playgroud)

然后客户端代码变得简单:

List<String> newList = join(listOne, listTwo);
Run Code Online (Sandbox Code Playgroud)

进一步阅读:https: //stackoverflow.com/a/924536/1064809

  • 这也是事实.滥用继承,而不是设置完全可用的数据属性(值/或集合项),是完全邪恶的. (3认同)
  • +1.唯一的答案指向**真正的**问题 (2认同)
  • +1表示在`static`方法中使用DBI的危害要小得多. (2认同)

das*_*ght 19

"丑陋"和"不在生产中使用"注释指的是匿名类的这种特定用法,而不是一般的匿名类.

这个特定的用法分配newList了一个匿名子类ArrayList<String>,一个全新的类,它是为了一个目的而创建的 - 即用两个特定列表的内容初始化一个列表.这不是非常易读(即使是经验丰富的读者也会花费几秒钟来解决它),但更重要的是,它可以在没有子类化的情况下实现相同数量的操作.

从本质上讲,该解决方案为创建新子类带来了一些小便利,这可能会导致问题,例如,当您尝试使用期望集合具有特定类型的自动化框架来持久保存此集合时.


Joa*_*uer 16

这种匿名类的特殊用法有几个问题:

  1. 这是一个鲜为人知的成语.在阅读和/或修改使用它的代码时,不了解它(或知道它不会大量使用它)的开发人员将会变慢.
  2. 它实际上是在滥用语言功能:你不是要尝试定义一种新的类型ArrayList,你只需要一些包含一些现有值的数组列表
  3. 它创建了一个占用资源的新类:用于保存类定义的磁盘空间,用于解析/验证/ ...的时间,用于保存类定义的permgen,...
  4. 即使"真实代码"略长,也可以轻松地将其移动到一个恰当命名的实用程序方法(joinLists(listOne, listTwo))

在我看来,#1是避免它的最重要原因,紧随其后的是#2.#3通常不是一个问题,但不应该被遗忘.

  • 当不需要继承时,它会误用继承. (3认同)
  • 谢谢Joachim.我的观点是#2是关键的一个 - 永远不应该使用继承,只需更改/设置可变对象的数据就可以正确地实现目标. (2认同)