【问题标题】:Java null arguments when chaining Constructors链接构造函数时的 Java 空参数
【发布时间】:2016-12-03 23:27:42
【问题描述】:

假设我有一个具有多个构造函数的类,其中一个是复制构造函数(用于复制对象):

public class Rectangle {

    int width, height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    public Rectangle(Rectangle source) {
        this(source.width, source.height);
    }
}

有什么方法可以检查复制构造函数中的source 是否为null,如果是则抛出IllegalArgumentException?因为另一个构造函数调用 必须 是我构造函数中的第一个语句。

【问题讨论】:

  • 为什么其他构造函数调用必须是复制构造函数中的第一个语句?
  • 因为这就是 Java 想要的。
  • @Janno:因为这就是 Java 的工作方式。你不能在另一个语句之后使用this(...)
  • null 被传递时,我会将其保留为 NPE。

标签: java constructor copy-constructor


【解决方案1】:

你可以这样做:

public Rectangle(Rectangle source) {
     this(checkNotNull(source, "Source cannot be null").width, source.height);
}

private static <T> T checkNotNull(T t, String msg) {
    if (t == null) throw new IllegalArgumentException(msg);
    return t;
}

我也同意 Jon Skeet 的观点,在这种情况下,NullPointerException 并不是一个坏行为。唯一的问题是,当您获得 NPE 时,在排长队中可能有点难以确定哪个对象是 null,这就是为什么更具体的消息会很有用。

如果您不费心抛出NullPointerException,也可以不重新发明轮子并使用标准的java.util.Objects 方法:

public Rectangle(Rectangle source) {
     this(Objects.requireNonNull(source, "Source cannot be null").width, source.height);
}

如果您的错误消息的构建成本很高,您可以提供Supplier&lt;String&gt;,以便仅在实际需要时支付构建消息的成本:

 public Rectangle(Rectangle source) {
     this(Objects.requireNonNull(source, () -> explainError(source)).width, source.height);
}

【讨论】:

  • checkNotNull 方法应返回“T”类型的值。
【解决方案2】:

如果你真的想抛出IllegalArgumentException,我认为最干净的解决方案是使用静态方法而不是构造函数:

public static Rectangle from(Rectangle source) {
    if (source == null) {
        throw new IllegalArgumentException("source can't be null!");
    }
    return new Rectangle(source.width, source.height);
}

或者你可以添加一个复制方法:

public Rectangle copy() {
    return new Rectangle(this.width, this.height);
}

我更喜欢后者,因为它无需担心 Rectangle 可能为空。请注意,如果您将其与空对象一起使用,这将导致 NPE,这可能进一步表明 NPE 很好。

【讨论】:

  • 请注意,copy-方法在继承的情况下可能会出现问题 - 所有子类必须覆盖它以避免意外行为。
【解决方案3】:

一个教科书技巧是将初始化从构造函数移到方法中。然后,你可以在它之前有任何你想要的代码:

public class Rectangle {

    int width, height;

    public Rectangle(int width, int height) {
        init(width, height);
    }

    public Rectangle(Rectangle source) {
        if (source == null) {
            throw new IllegalArgumentException("source can't be null!");
        }
        init(source.width, source.height);
    }

    private void init(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

【讨论】:

  • 请注意,这意味着 widthheight 不能是最终的,这通常是一个非常重要的缺点。
  • 另外,private final 是多余的,因为无法从子类中看到 private 方法
  • @Dici 是的,没错,删除了final
  • @JonSkeet 有趣的一点是关于这种禁止成员最终确定的技术。谢谢!
【解决方案4】:

你可以这样做

int width, height;

public Rectangle(int width, int height) {
    this.width = width;
    this.height = height;
}

public Rectangle(Rectangle source) {
   if(source != null) {
      width = source.width;
      height = source.height;
   }
}

【讨论】:

  • 他想通过调用基本构造函数并抛出验证异常来避免代码重复。此代码不满足这两个要求
  • 啊,好的,非常感谢。对不起,这是我在这里的第一个答案。
  • 不用担心 :)。 Stack Overflow 上的美好旅程
【解决方案5】:

是的,您可以使用辅助方法,该方法将在必要时抛出异常,否则返回原始值...您可以在构造函数调用中调用它,因为您允许方法调用作为参数评估的一部分。

// In a helper class
public static <T> T checkNotNull(T value) {
    if (value == null) {
        throw new IllegalArgumentException();
    }
    return value;
}

然后将其用作:

public Rectangle(Rectangle source) {
    this(Helper.checkNotNull(source).width, source.height);
}

但是...我相信 NullPointerException 无论如何都是推荐的抛出异常(例如在 Effective Java 2nd edition 中),您现有的代码已经抛出该异常。因此,您很可能不想对现有代码进行任何更改。

如果你想要一个辅助方法来进行这样的检查,但很高兴它抛出 NullPointerException,我建议使用 Guava 和它的 Preconditions 类,它有这个和一个 lot其他有用的检查方法。

另请注意,Java 1.7 引入了 java.util.Objects,其中包含 requireNonNull,因此您甚至不需要第三方库。

【讨论】:

  • NullPointerException relly 是要走的路吗?我以为以前见过IllegalArgumentException被抛出就是这样的情况。
  • @kalsowerus java.util.Objects.requireNonNull 抛出 NullPointerException,所以是的,没关系。查看我的答案以查看一些示例(在我编辑之后)
  • @kalsowerus NPE 表示您在本应使用对象引用时使用了 null 值。
  • @kalsowerus:NullPointerException 是 Josh Bloch 推荐的,也是 Google 的样式指南使用的。这是我对这些事情的主要真相来源:)
  • @Dici:哦,我没见过Objects.requireNonNull。将在此处进行编辑。
猜你喜欢
  • 2015-07-24
  • 1970-01-01
  • 1970-01-01
  • 2013-01-31
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 2011-09-07
  • 2021-03-12
相关资源
最近更新 更多