【问题标题】:When did add() method add object in collectionadd() 方法何时在集合中添加对象
【发布时间】:2019-03-12 09:03:44
【问题描述】:

我有两个问题。首先,考虑下面的代码。

public class Test{
    private static final List<String> var = new ArrayList<String>() {{
        add("A");
        add("B");
        System.out.println("INNER : " + var);
    }};

    public static void main(String... args){
        System.out.println("OUTER : " + var);
    }   
}

当我运行这段代码时,它会给出下面的输出

INNER : null
OUTER : [A, B]

任何人都可以详细说明为什么INNER 为空,并且在将“A”和“B”添加到collection 时执行流程?

其次,我对上面的代码做了一些修改,修改为下面的(只需将add方法放在第一个括号内)

public class Test{
    private static final List<String> var = new ArrayList<String>() {
        public boolean add(String e) { 
            add("A");
            add("B");
            System.out.println("INNER : " + var); return true; 
        };
    };

    public static void main(String... args){
        System.out.println("OUTER : "+var);
    }   
}

运行上面的代码后,我得到了以下结果

OUTER : []

看完之后,我完全不知道这里发生了什么。 INNER去哪儿了?为什么不打印?不叫吗?

【问题讨论】:

  • 这个 btw 是一个很大的反模式
  • 需要使用"INNER " + this打印出ArrayList的当前状态,var此时没有赋值。

标签: java arraylist collections static


【解决方案1】:
  1. 因为ArrayList 之外的匿名类的初始化块被执行之前匿名类的实例引用被分配给var

  2. 代码未执行,因为您从未调用过add(String e) 方法。如果你这样做了,它会产生一个StackOverflowError,因为add("A") 现在是一个递归调用。

【讨论】:

  • 如果我从 main 方法调用 add() 它是如何变成递归调用的(我检查它,因为你提到它给出了 StackOverflowError)
  • @RBSadd(String e)里面的第一条语句是add("A"),它是对自身的调用,所以它执行add("A"),它是对自身的调用,所以它执行add("A"),这是对自身的调用,所以它执行add("A"),这是对自身的调用,所以它执行add("A"),这是对自身的调用,所以它执行add("A"),以及StackOverflowError.
【解决方案2】:

在您的代码 sn-p 1 中,您使用 双括号初始化 将项目添加到数组列表中。它基本上是一个带有实例初始化器的匿名内部类。由于您在构造对象之前打印var,因此它是null

在您的第二个 sn-p 中,您正在创建一个匿名类 ArrayList,它提供了 add 方法的实现。但是没有人调用 ArrayList 对象上的add 方法。

编辑:Andreas@ 提出的好观点 - 即使您调用 add 方法,它也会导致无限递归调用,从而导致 StackOverflowError

参考:

Initialization of an ArrayList in one line

【讨论】:

    【解决方案3】:
    1. 你创建的对象在初始化过程中没有分配给var,试试这个:

      private static final List<String> var = new ArrayList<String>() {{
          add("A");
          add("B");
          System.out.println("THIS : " + this); // THIS : [A, B]
          System.out.println("INNER : " + var); // INNER : null
      }};
      
    2. 你只是重写ArrayListadd 方法,它被调用了。它的工作原理是:

      static class CustomList<E> extends ArrayList<E> {
      
          @Override
          public boolean add(String e) {
              add("A");
              add("B");
              System.out.println("INNER : " + var);
              return true;
          }
      }
      
      private static final List<String> var = new CustomList<>();
      

    【讨论】:

      【解决方案4】:

      在第一个代码中,当您添加元素并在实例初始化程序中打印列表时,在其构造函数返回之前打印变量。 所以它只能打印null

      在第二个代码中,您覆盖 add() 以添加硬编码元素,但从未在创建的实例上调用 add()。所以add() 不会打印任何东西,也不会在 List 实例中添加任何内容。

      要在 List 中添加元素,您通常需要在 List 引用上调用 add(),例如:

      List<String> list = new ArrayList<>();
      list.add("element A");
      list.add("element B");
      

      您不会为此类要求创建匿名类。

      如果你想使用更简单的语法,你仍然可以这样做:

      List<String> list = new ArrayList<>(Arrays.asList("element A", "element B"));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-16
        • 1970-01-01
        • 1970-01-01
        • 2014-12-09
        • 1970-01-01
        • 2012-07-07
        相关资源
        最近更新 更多