关于java:我如何修复这些双括号

How do I fix these double braces

本问题已经有最佳答案,请猛点这里访问。

你好,我正在整理一些代码,我偶然发现了一些双括号。我意识到这是一种罪恶,我应该改正它。但是,我不知道从哪里开始。有人能帮忙吗?

1
2
3
4
5
6
7
8
9
10
private SelectItem notifyTypeItem = new SelectItem();

notifyTypeItem.setTitle("Default Notification");
    notifyTypeItem.setWidth("100%");
    notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
                   put("0","None");
                   put("1","Subtle");
                   put("2","Intrusive");
               }}
    );


为了理解如何修复它,您应该首先了解它在做什么。为此,您需要了解两件事:

  • 匿名子类
  • 实例初始值设定项块
  • 如何修复它的tl;dr只是将那些put调用和初始化从setter中分离出来:

    1
    2
    3
    4
    5
    Map<String, String> valueMap = new LinkedHashMap<String, String>();
    valueMap.put("0","None");
    valueMap.put("1","Subtle");
    valueMap.put("2","Intrusive");
    notifyTypeItem.setValueMap(valueMap);

    请继续阅读,了解正在发生的事情以及为什么这可能是一种糟糕的方法。

    匿名子类

    匿名类通常只是一个没有名称的类。例如,您可以创建接口的匿名实例,如Runnable

    1
    2
    3
    4
    5
    6
    7
    Runnable r = new Runnable() {
        @Override
        public void run() {
            // Do something
        }
    };
    r.run(); // Does that something

    同样,您也可以创建抽象类和具体类的匿名实例。例如,创建ThreadLocal的匿名实例非常常见:

    1
    2
    3
    4
    5
    6
    7
    private static ThreadLocal<SimpleDateFormat> localIsoDateFormat = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    }

    当您不需要一个完整的专用类来重写一两个方法时,这非常有用,类似于只使用一个方法创建接口的匿名实例。

    实例初始值设定项块

    实例初始值设定项块允许您在构造函数之外执行初始值设定项。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class MyClass {

        private final String s;
        {
            s ="My Class String";
        }

        public String getS() { return s; }
    }

    它本质上是一个构造函数的代替品,通常是不必要的,所以您很少看到它。它几乎总是可以移动到构造函数。

    组合它们

    您的示例结合了它们。它正在创建LinkedHashMap的匿名子类,然后还使用初始化器块。更正确的格式,您的代码是:

    1
    2
    3
    4
    5
    6
    7
    Map<String, String> map = new LinkedHashMap<>() {
        {
            put("0","None");
            put("1","Subtle");
            put("2","Intrusive");
        }
    };

    它是LinkedHashMap的匿名实例,具有执行put调用的实例初始值设定项块。

    为什么不好?

    出于同样的原因,您需要小心创建匿名类:对封闭类实例的引用。

    匿名类因是应用程序内存泄漏的源而臭名昭著。您的代码似乎位于非static上下文中。这意味着您创建的匿名LinkedHashMap子类将隐式引用您的方法所在的类。例如,如果您的方法在MyClass中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class MyClass {
        private SelectItem notifyTypeItem = new SelectItem();

        public void foo() {
            notifyTypeItem.setTitle("Default Notification");
            notifyTypeItem.setWidth("100%");
            notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
                       put("0","None");
                       put("1","Subtle");
                       put("2","Intrusive");
                   }}
            );
        }
    }

    新创建的LinkedHashMap子类(MyClass$1将是"类名",如果可以这样称呼它的话)将引用封闭的MyClass实例。在某些情况下,这可能是好的。但是,如果您创建notifyTypeItem的目的是将其传递给其他对象,并丢弃MyClass实例,那么您将在应用程序中创建内存泄漏。MyClass实例将被MyClass$1实例引用,SelectItem将引用MyClass$1实例,因此在SelectItem实例不再被引用之前,MyClass实例将永远不会被垃圾收集。如果MyClass除了SelectItem之外还有其他几个引用,那么这只会增加单个MyClass实例消耗的总内存,并导致更多的内存泄漏问题。

    工具书类

    以下是一些相关链接:

    • 匿名类Java TRAIL(Java TRAIL,DOCS.Oracle .com)
    • 初始化字段Java TRAIL(Java TRAIL,DOCS.Oracle .com)
    • Java中双括号初始化是什么?(所以问题,stackoverflow.com)
    • 为什么不应该使用双括号初始值设定项(nish-tahir的blog post,blog.nishtahir.com)
    • 什么时候使用(匿名)内部类才是安全的?(所以问题,stackoverflow.com)