简体   繁体   English

链接构造函数时的Java null参数

[英]Java null arguments when chaining Constructors

Let's say I have a class with multiple constructors, one of which is a copy-constructor (to copy an object): 假设我有一个包含多个构造函数的类,其中一个是复制构造函数(用于复制对象):

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);
    }
}

Is there any way I can make check if source is null in the copy-constructor and throw an IllegalArgumentException if it is? 有没有什么办法可以检查copy-constructor中的source是否为null ,如果是,则抛出IllegalArgumentException Because the other constructor call has to be the first statement in my constructor. 因为其他构造函数调用必须是我的构造函数中的第一个语句。

You can do this: 你可以这样做:

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;
}

I also agree with Jon Skeet that a NullPointerException is not a bad bevahiour in this case. 我同意Jon Skeet的观点,在这种情况下, NullPointerException并不是一个坏的bevahiour。 The only thing is that in long lines when you get an NPE it can be a bit hard to identify which object is null , which is why a more specific message can be useful. 唯一的问题是,在获得NPE的情况下排长队时,确定哪个对象为null可能有点困难,这就是更具体的消息可能有用的原因。

You can also not reinvent the wheel and use standard java.util.Objects methods if you don't bother throwing a NullPointerException instead: 如果你不打扰抛出NullPointerException你也不能重新发明轮子并使用标准的java.util.Objects方法:

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

if your error message is expensive to build, you can provide a Supplier<String> instead, to pay the cost of the construction of the message only when it's actually needed: 如果您的错误消息构建起来很昂贵,则可以提供Supplier<String> ,以便仅在实际需要时支付构造消息的成本:

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

Yes, you can use a helper method which will throw the exception if necessary, and return the original value otherwise... you can call that within your constructor invocation, as you're allow method calls as part of argument evaluation. 是的,您可以使用辅助方法,如果需要将抛出异常,否则返回原始值...您可以在构造函数调用中调用它,因为您允许方法调用作为参数评估的一部分。

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

Then use it as: 然后用它作为:

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

However... I believe that NullPointerException is the recommended exception to throw here anyway (in Effective Java 2nd edition, for example), which your existing code will throw already. 但是...我相信NullPointerException是建议的异常,无论如何都要扔在这里(例如,在Effective Java第2版中),你的现有代码已经抛出了。 So you quite possibly don't want to make any change to your existing code. 因此,您可能希望对现有代码进行任何更改。

If you want a helper method for checks like this but are happy for it to throw NullPointerException , I'd recommend using Guava and its Preconditions class, which has this and a lot of other helpful checking methods. 如果你想要一个像这样的检查的帮助器方法,但很高兴它抛出NullPointerException ,我建议使用Guava及其Preconditions类,它有这个以及许多其他有用的检查方法。

Also note that Java 1.7 introduced java.util.Objects which has requireNonNull , so you don't even need a third party library. 另请注意,Java 1.7引入了具有requireNonNull java.util.Objects ,因此您甚至不需要第三方库。

One text-book trick is move the initialization out of the constructor to a method. 一个教科书技巧是将初始化从构造函数移到方法中。 Then, you can have whatever code you want before it: 然后,你可以拥有你想要的任何代码:

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;
    }
}

If you really do want to throw an IllegalArgumentException , I think the cleanest solution is to use a static method instead of a constructor: 如果你确实想要抛出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);
}

Or you could just add a copy method: 或者你可以添加一个复制方法:

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

I'd prefer the latter, since it removes the need to concern yourself with the Rectangle possibly being null. 我更喜欢后者,因为它不需要关注Rectangle可能为null。 Note that this will cause a NPE if you use this with a null Object, which is possibly a further indication that an NPE is fine. 请注意,如果将此对象与null对象一起使用,则会导致NPE,这可能进一步表明NPE正常。

You can do like this 你可以这样做

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;
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM