Java 链接构造函数时的空参数
Java null arguments when chaining Constructors
假设我有一个具有多个构造函数的 class,其中一个是复制构造函数(用于复制一个对象):
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
?因为另一个构造函数调用 必须 成为我的构造函数中的第一条语句。
你可以这样做:
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<String>
来代替,以便仅在实际需要时支付构建消息的成本:
public Rectangle(Rectangle source) {
this(Objects.requireNonNull(source, () -> explainError(source)).width, source.height);
}
是的,您可以使用辅助方法,它会在必要时抛出异常,否则 return 原始值...您可以在构造函数调用中调用它,因为您允许方法调用作为参数评估的一部分。
// 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 第 2 版中),您现有的代码已经抛出该异常。因此,您很可能不想对现有代码进行任何更改。
如果你想要一个像这样的检查辅助方法,但很高兴它抛出 NullPointerException
,我建议使用 Guava 及其 Preconditions class,它有这个以及 很多 其他有用的检查方法。
另请注意,Java 1.7 引入了 java.util.Objects
,其中包含 requireNonNull
,因此您甚至不需要第三方库。
一个教科书技巧是将初始化从构造函数移到方法中。然后,你可以在它之前添加任何你想要的代码:
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;
}
}
你可以这样做
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;
}
}
如果你真的想抛出一个 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 没有问题。
假设我有一个具有多个构造函数的 class,其中一个是复制构造函数(用于复制一个对象):
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
?因为另一个构造函数调用 必须 成为我的构造函数中的第一条语句。
你可以这样做:
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<String>
来代替,以便仅在实际需要时支付构建消息的成本:
public Rectangle(Rectangle source) {
this(Objects.requireNonNull(source, () -> explainError(source)).width, source.height);
}
是的,您可以使用辅助方法,它会在必要时抛出异常,否则 return 原始值...您可以在构造函数调用中调用它,因为您允许方法调用作为参数评估的一部分。
// 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 第 2 版中),您现有的代码已经抛出该异常。因此,您很可能不想对现有代码进行任何更改。
如果你想要一个像这样的检查辅助方法,但很高兴它抛出 NullPointerException
,我建议使用 Guava 及其 Preconditions class,它有这个以及 很多 其他有用的检查方法。
另请注意,Java 1.7 引入了 java.util.Objects
,其中包含 requireNonNull
,因此您甚至不需要第三方库。
一个教科书技巧是将初始化从构造函数移到方法中。然后,你可以在它之前添加任何你想要的代码:
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;
}
}
你可以这样做
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;
}
}
如果你真的想抛出一个 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 没有问题。