匿名类和父类之间的循环依赖是否错误?

时间:2015-06-08 12:07:46

标签: java anonymous-class anonymous-inner-class cyclic-reference

我有以下代码片段:

android:windowSoftInputMode="adjustPan|adjustResize"

Theres是匿名public class Example { private Integer threshold; private Map<String, Progress> history; protected void activate(ComponentContext ctx) { this.history = Collections.synchronizedMap(new LinkedHashMap<String, Progress>() { @Override protected boolean removeEldestEntry(Map.Entry<String, Progress> entry) { return size() > threshold; } }); } } 类和LinkedHashMap类之间的循环依赖关系。这样可以吗?为什么不?它会被垃圾收集器很好地回收吗?

5 个答案:

答案 0 :(得分:22)

  

这样可以吗?

这完全没问题。

threshold是一个字段,因此可以在匿名类中引用它而没有任何问题。 (如果threshold是一个局部变量,它必须(有效地)最终。)

类之间的循环依赖关系是常见的,当依赖关系图很小时(如本例所示),它不会造成任何问题。您LinkedHashMap是匿名课程的事实在这里并不重要。

  

它会被垃圾收集器很好地回收吗?

关于内存泄漏+内部类的唯一警惕是(非静态)内部类具有对其封闭对象的隐式引用。这意味着如果你创建了很多很多内部类的实例,你就不能期望外部类对象的实例被垃圾收集。

在这种情况下,这意味着如果您泄漏对history地图的引用,Example的实例将不会被GC修改。

相关说明:

  • 考虑到您正在使用synchronizedMap,您似乎正在研究多线程程序。如果是这种情况,您需要警惕threshold字段的同步和可见性问题。

  • 如果可能,请尝试将threshold字段设为最终

  • 另一种选择是为LinkedHashMap创建一个命名类,并将threshold包含在该类中作为字段。

答案 1 :(得分:6)

无论如何,你都有这种依赖,因为匿名内部类的每个对象都隐含了对封闭类对象的引用。 Java就是这样设计的,嵌套的内部类有这个参考是有原因的,因此从语言规范的角度来看,这会编译并看起来非常正常。

关于(缺少)&#34;设计气味&#34;,如果这个匿名类对象完全封装在Example类中,没有独特的意义没有其封闭的上下文,并且不会泄露到外面的任何地方对于Example类,引用封闭类的字段没有任何问题。您只需使用此内部类对某些逻辑进行分组。

但是,如果此对象从封闭对象中泄漏(例如,通过getter返回),则应禁止此操作或将其重构为接收threshold作为参数的静态内部类。此内部对象保存对封闭对象的引用,并可能使其远离GC,从而导致内存泄漏。

答案 2 :(得分:2)

循环依赖本身并不坏,但它可能会导致一些意外的内存泄漏。

按原样拍摄你的例子,它现在很好,因为它可以做你想做的事。

但是,如果您或其他人修改您的代码以公开您的私人代码:

private Map<String, Progress> history;

然后你可能有麻烦。会发生什么事情,你也会传递对Example类的引用,无论是否有意,因为你的内部类有隐式引用它。

我现在不能给你直接引用,但Steve McConnell在他的代码中完成了将循环依赖项称为反模式。你可以在那里阅读或者我猜谷歌,非常详细地阅读。

我能想到的另一个问题是,循环依赖相当难以进行单元测试,因为您在对象之间创建了非常高级别的耦合。

一般情况下,你应该避免循环依赖,除非你有充分的理由不这样做,比如实现循环链​​表。

答案 3 :(得分:2)

每当你实例化一个非静态的内部类(无论是命名还是匿名)时,这个内部类都会出现。 instance自动获取对封闭父类实例的引用。

以上意味着如果外部类也包含对非静态内部类的引用(如代码中的情况),则外部类的实例与非静态内部类之间存在循环依赖关系(再次,命名和匿名)。

此设置中唯一的实际问题是您对此现有交叉引用的使用是否合法。在您的特定情况下,我没有看到任何问题 - 非静态内部类使用封闭外部类的实例变量。对我来说似乎是犹太人。

在这种情况下,内部泄漏通常发生在对内部类的实例的引用传递到外部类之外时(通常是各种Listeners的情况) - 因为此实例具有对外部类的实例,外部类不能被垃圾收集。但是,如果您只是交叉引用外部类和内部类,我不会认为会导致内存泄漏 - 它们将被垃圾收集在一起。

答案 4 :(得分:1)

我不喜欢你的解决方案(即使我同意这可行):

  1. 您的类示例应该实现Map或扩展LinkedHashMap,因为在那里定义了实例变量阈值并使用自己的定义优化了LinkedHashMap的概念。

  2. 您的类示例不应实现Map或扩展LinkedHashMap,因为activate方法不会优化LinkedHashMap或Map,而是使用Maps的概念。

  3. 1 + 2 =&gt;受孕问题。