【问题标题】:Should I keep instance variables in Java always initialized or not?我是否应该始终初始化 Java 中的实例变量?
【发布时间】:2009-02-13 09:53:26
【问题描述】:

我最近开始了一个新项目,我试图让我的实例变量始终初始化为某个值,因此它们在任何时候都不为空。下面的小例子:

public class ItemManager {

  ItemMaster itemMaster;
  List<ItemComponentManager> components;

  ItemManager() {
    itemMaster = new ItemMaster();
    components = new ArrayList<ItemComponentManager>();
  }

  ...
}

重点主要是避免在代码中某处使用实例变量之前对 null 进行繁琐的检查。到目前为止,它运行良好,并且您大多不需要 null-值,因为您还可以检查空字符串或空列表等。我没有将这种方法用于方法范围的变量它们的范围非常有限,因此不会影响代码的其他部分。

这一切都是实验性的,所以我想知道这种方法是否可行,或者是否有一些我还没有看到的陷阱。保持初始化实例变量通常是个好主意吗?

【问题讨论】:

  • 永远不要创建一个空的 ArrayList 只是为了以后扔掉它。始终使用 Collections 类中的一种方法来获取一个空的、不可变的集合。

标签: java variables initialization


【解决方案1】:

我通常将空集合和空集合视为两个独立的东西:

空集合意味着我知道可用的项目为零。 null 集合会告诉我我不知道集合的状态,这是另一回事。

所以我真的不认为这是非此即彼的。如果我在构造函数中初始化它们,我会声明变量 final 。如果您将其声明为 final,那么读者将非常清楚此集合不能为空。

【讨论】:

    【解决方案2】:

    首先,如果您想保留控制权,所有非最终实例变量必须声明为私有

    也考虑惰性实例化——这也避免了“坏状态”,但只在使用时初始化:

    class Foo {
        private List<X> stuff;
        public void add(X x) {
            if (stuff == null)
                stuff = new ArrayList<X>();
            stuff.add(x);
        }
        public List<X> getStuff() {
            if (stuff == null)
                return Collections.emptyList();
            return Collections.unmodifiableList(stuff);
        }
    }
    

    (注意 Collections.unmodifiableList 的使用——除非你真的希望调用者能够从你的列表中添加/删除,你应该让它不可变)

    考虑将创建多少个相关对象的实例。如果有很多,并且您总是创建列表(并且最终可能会得到许多空列表),那么您可能会创建比您需要的更多的对象。

    除此之外,这真的是一个品味问题,以及你在构建时是否可以拥有有意义的价值。

    如果您正在使用 DI/IOC,您希望框架为您完成工作(尽管您可以通过构造函数注入来完成;我更喜欢 setter) -- 斯科特

    【讨论】:

    • 真的,你认为所有的成员变量都应该是私有的吗?受保护,是的。私人的,没有。子类化呢?
    • 子类可以将它们设置为 null。
    • 实例字段应该是私有的以便正确封装,从不保护。
    • 我应该澄清我的意思是所有非静态实例变量。子类应该像其他类一样访问它们——通过访问器。这允许基类保持其状态一致。
    • (d'oh - 我的意思是非决赛。需要我早上醒来的药物)
    【解决方案3】:

    我会说这完全没问题 - 只要您记得那里有“空”占位符值而不是真实数据。

    将它们保持为 null 的好处是迫使您处理它们 - 否则程序会崩溃。如果您创建了空对象,但忘记了它们,您会得到未定义的结果。

    并且只是对防御性编码进行评论 - 如果您是创建对象的人并且从不将它们设置为 null,则无需每次都检查 null。如果由于某种原因你得到空值,那么你就知道发生了灾难性的错误,程序无论如何都会崩溃。

    【讨论】:

    • 我赞同关于防御性编码的批评意见。很多时候,“防守”只会隐藏逻辑错误,而且我经常看到防守变得糟糕。我很少会因为快速失败的态度而出现问题。
    • “防御性”不应通过适当的错误报告隐藏任何逻辑错误。如果您检查 null 并在遇到 null 时报告错误,那么您就处于防御状态,您可以快速轻松地发现逻辑错误,而不会杀死生产应用程序。
    【解决方案4】:

    如果可能的话,我会让它们final。然后它们必须在构造函数中初始化,并且不能为空。

    在任何情况下,您还应该将它们设为私有,以防止其他类将 null 分配给它们。如果您可以检查您的班级中从未分配过 null,那么该方法将起作用。

    【讨论】:

    • 决赛!?这使得进行任何临时(反)序列化变得困难。持久化到数据库变得很困难。扩展课程也变得很痛苦。
    • @Pat YAGNI(你不需要它)。子类无论如何都不能访问超类的私有状态,这是一件好事。
    【解决方案5】:

    我遇到过一些导致问题的案例。 在反序列化期间,一些框架不会调用构造函数,我不知道他们如何或为什么选择这样做,但它确实发生了。这可能导致您的值为null。我也遇到过调用构造函数但由于某种原因没有初始化成员变量的情况。

    实际上,我会使用以下代码而不是您的代码:

    public class ItemManager {
    
      ItemMaster itemMaster = new ItemMaster();
      List<ItemComponentManager> components = new ArrayList<ItemComponentManager>();
    
      ItemManager() {
         ...
      }
      ...
    }
    

    【讨论】:

      【解决方案6】:

      我处理我声明的任何变量的方式是决定它是否会在对象(或类,如果它是静态的)的生命周期内发生变化。如果答案是“否”,那么我将其定为最终结果。

      使它最终强制你在创建对象时给它一个值......我个人会做以下事情,除非我知道我会改变重点:

      私人最终ItemMaster itemMaster; 私有最终列表组件; // 实例初始化块 - 发生在构造时 { itemMaster = new ItemMaster(); 组件 = 新的 ArrayList(); }

      您的代码现在的方式是您必须始终检查 null,因为您没有将变量标记为私有(这意味着同一包中的任何类都可以将值更改为 null)。

      【讨论】:

      • 或者只是私有最终ItemMaster itemMaster = new ItemMaster();私有最终列表组件 = new ArrayList();
      • 我喜欢将初始化与声明分开。
      【解决方案7】:

      是的,在构造函数中初始化所有类变量是个好主意。

      【讨论】:

        【解决方案8】:

        重点主要是避免 使用前繁琐的 null 检查 某处的类变量 代码。

        您仍然需要检查是否为空。第三方库甚至 Java API 有时会返回 null。

        另外,实例化一个可能永远不会使用的对象是一种浪费,但这取决于你的类的设计。

        【讨论】:

          【解决方案9】:

          对象在构建后应该 100% 准备就绪。用户不必检查空值。防御性编程是要走的路——保持检查。

          为了 DRY,您可以将检查放在 setter 中,然后让构造函数调用它们。这样您就不会对检查进行两次编码。

          【讨论】:

          • 小心从构造函数调用setter——如果子类覆盖了setter,你可能会在子类初始化之前调用子类代码!
          • 但是如果我在类 A 的构造函数中,并且它调用自己的设置器,那么覆盖它们的子类 B 有什么关系呢? “this”指向 A 的一个实例,所以我会在其构造函数中调用 A 设置器,而不是 B。真的吗?
          • @duffymo 'this' 在构造函数中总是指正在构造的对象。 'this' 的静态类型是它出现的类。运行时类型可以是同一类或该类的任何子类。
          • 如果子类覆盖添加事件支持怎么办?例如,子类 setFoo() 可能会调用尚未初始化的 propertyChangeSupport。
          【解决方案10】:

          如果这是您的所有代码并且您想设置该约定,那么拥有它应该是一件好事。不过,我同意 Paul 的评论,没有什么可以阻止某些错误代码意外地将您的类变量之一设置为 null。作为一般规则,我总是检查空值。是的,这是一个 PITA,但防御性编码可能是一件好事。

          【讨论】:

            【解决方案11】:

            从“ItemManager”类的名称来看,ItemManager 在某些应用程序中听起来像是一个单例。如果是这样,您应该进行调查,并且真的,真的,知道依赖注入。使用 Spring (http://www.springsource.org/) 之类的东西来创建 ItemComponentManagers 列表并将其注入 ItemManager。

            如果没有 DI,在严肃的应用程序中手动初始化是一场噩梦,要调试并连接各种“管理器”类来进行狭窄的测试是地狱。

            始终使用 DI(即使在构建测试时)。对于数据对象,创建一个 get() 方法,如果列表不存在则创建该列表。但是,如果对象很复杂,几乎可以肯定的是,使用 Factory 或 Builder 模式会让您的生活变得更好,并让 F/B 根据需要设置成员变量。

            【讨论】:

              【解决方案12】:

              如果在您设置的一种方法中会发生什么

              itemMaster = null; 
              

              或者您将 ItemManager 的引用返回给某个其他类,并将 itemMaster 设置为 null。 (您可以防止这很容易返回您的 ItemManager 等的克隆)

              我会尽可能保留支票。

              【讨论】:

              • 我在设计上要避免设置空值(作为不成文的规则):没有方法会调用“itemMaster = null”
              • 并且可以使用 final 强制重置引用(对于可变类)。
              • 外部代码设置它对 itemMaster 的引用将无效。它只会删除对对象的本地引用。它不会清除对象的内部引用或破坏对象。
              猜你喜欢
              • 2013-07-04
              • 2013-08-14
              • 1970-01-01
              • 2017-01-26
              • 2014-09-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-07
              相关资源
              最近更新 更多