在阅读question - How do I join two lists in java的答案时出现了问题。这个answer提供了解决方案
List<String> newList = new ArrayList<String>() { { addAll(listOne); addAll(listTwo); } };
阅读评论,用户说它是邪恶和丑陋的,不应该用于生产。
我想知道使用它有什么害处?为什么在生产中使用它是丑陋的,邪恶的还是坏的?
注意:被问到这是一个问题因为,引用的帖子太旧了(2008年),而且应答者已经离开几个月了。
答案 0 :(得分:47)
除了已经提到的关于良好编程风格和继承滥用的问题之外,还有一个更微妙的问题 - 内部类和(非静态)匿名类实例充当闭包。这意味着它们保持对封闭类实例的隐式引用。这可以防止垃圾收集,最终导致内存泄漏。
给出一段示例源代码:
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();
}
}
}
在编译时会发生什么,是编译器为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();
}
}
即使对于永远不会实际访问封闭类的任何方法或字段的匿名类,也会发生对封闭实例的引用的捕获,例如您的双括号初始化(DBI)列表问题
这导致DBI列表保留对封闭实例的引用,只要它存在,从而防止封闭实例被垃圾回收。假设DBI列表恰好在应用程序中存在了很长时间,例如作为MVC模式中模型的一部分,并且捕获的封闭类例如是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;
}
然后客户端代码变得简单:
List<String> newList = join(listOne, listTwo);
答案 1 :(得分:18)
&#34;丑陋&#34;并且&#34;不要在生产中使用&#34;注释是指匿名类的这种特定用法,而不是一般的匿名类。
此特定用法为newList
分配ArrayList<String>
的匿名子类,这是一个全新的类,只需一个目的即可创建 - 即初始化包含两个特定列表内容的列表。这不是非常易读(即使是经验丰富的读者也花费几秒钟来计算它),但更重要的是,它可以在没有子类化的情况下实现相同数量的操作。
本质上,该解决方案为创建新子类带来了一些小便利,这可能会导致问题,例如,当您尝试使用自动化框架来持久保存此集合时,该框架期望集合具有特定类型
答案 2 :(得分:16)
匿名类的这种特殊用法有几个问题:
ArrayList
,你只是想要一些包含一些现有值的数组列表joinLists(listOne, listTwo)
)在我看来,#1是避免它的最重要原因,紧随其后的是#2。 #3通常不是一个问题,但不应该被遗忘。
答案 3 :(得分:4)
因为你不需要一个单独的子类 - 你只需要创建一个普通类的新ArrayList,并且addAll()
都列入它。
像这样:
public static List<String> addLists (List<String> a, List<String> b) {
List<String> results = new ArrayList<String>();
results.addAll( a);
results.addAll( b);
return results;
}
创建子类是不好的,这是不需要的。您不需要扩展或子类行为 - 只需更改数据值。
答案 4 :(得分:2)
这本身并不是一种糟糕的方法,比如说,在性能或类似的东西,但这种方法有点模糊,当使用这样的东西时,你总是(比方说,99%)必须解释这种方法。我认为这是不使用这种方法的最大原因之一,并且在键入时:
List<String> newList = new ArrayList<String>();
newList.addAll(listOne);
newList.addAll(listTwo);
是一个更多的输入,它更容易阅读,这有助于理解或调试代码。
答案 5 :(得分:1)
在你的例子里,它真的看起来像是邪恶和丑陋的,至少对我来说 - 很难理解代码中发生了什么。但是有一些模式使用人们习惯的匿名类,因为他们经常看到它们,例如
Arrays.sort(args, new Comparator<String>() {
public int compare(String o1, String o2) {
return ...
}});
我会将上述内容称为最佳实践案例。