在 Java 中,在构造函数中调用 super(...) 时避免 null 取消引用

问题描述 投票:0回答:2

我正在使用 Java 21。

我有两节课:

abstract class MySuperClass {
    private final Object mySuperField;
    MySuperClass(Object myField) {
        this.mySuperField = myField;
    }
    public Object getMySuperField() {
        return mySuperField;
    }
}
public class MySubClass extends MySuperClass {
    private final Object mySubField;
    public MySubClass(MySubClass toCopy) {
        super(toCopy.getMySuperField());
        this.mySubField = toCopy.mySubField;
    }
}

MySubClass
有一个复制构造函数,如上所示。

如果

NullPointerException
参数为 null,我想避免在
MySubClass
构造函数中抛出
toCopy
,而宁愿抛出我自己的异常。但当然,对
super(...)
的调用必须是构造函数中的第一行。

在调用

super(...)
之前,我可以使用一些 Java 模式来进行自己的参数验证吗?

我尝试了一些不同的方法,但它们看起来都很丑陋/老套,比如将 null 传递到超类构造函数中,而不在超类中进行验证,然后在子类中调用

super(...)
后进行参数验证,或者使
mySuperField
中的
MySuperClass
不是最终的,并在超类中提供一个 setter。一定有更好的东西。

在我尝试过的所有方法中,这可能是最好的选择,但它仍然感觉很糟糕。

public class MySubClass extends MySuperClass {
    private final Object mySubField;
    public MySubClass(MySubClass toCopy) {
        super(toCopy == null ? null : toCopy.getMySuperField());
        if (toCopy == null) {
            // Throw my exception
        }
        this.mySubField = toCopy.mySubField;
    }
}
java constructor nullpointerexception subclass super
2个回答
6
投票

向超类添加复制构造函数

如果您控制超类并且可以修改它,那么我相信最干净的解决方案是向超类添加复制构造函数。

// could have protected visibility (or even package-private if all
// subclasses are in the same package)
public MySuperClass(MySuperClass toCopy) {
  if (toCopy == null) {
    // throw exception
  }
  // ...
}

那么子类的复制构造函数将如下所示:

public MySubClass(MySubClass toCopy) {
  super(toCopy); // won't return normally if 'toCopy' is null
  // ...
}

使用静态方法进行验证

如果您无法修改超类来执行验证,或者如果您委托给同一个类中的另一个构造函数并且必须预先验证参数,那么一个相对常见的解决方案是使用静态方法来执行验证.

public class MySubClass extends MySuperClass {

  private static MySubClass validate(MySubClass arg) {
    if (arg == null) {
      // throw exception
    }
    return arg;
  }

  public MySubClass(MySubClass toCopy) {
    // 'validate' won't return normally if 'toCopy' is null
    super(validate(toCopy).getMySuperField());
    // ...
  }
}

注意

validate
的作用与
Objects::requireNonNull(T)
类似。如果您发现自己执行许多类似的验证,那么您可以为此创建自己的实用程序类。或者找到一个为您提供此类实用程序类的现有库。另外,如果您不介意抛出 NPE 但带有自定义消息,那么您可以使用
Objects::requireNonNull(T,String)
Objects::requireNonNull(T,Supplier)
重载。

预览功能

如果您不介意使用预览功能,请查看:

这将允许您在子类的构造函数中执行以下操作:

public MySubClass(MySubClass toCopy) {
  if (toCopy == null) {
    // throw exception
  }
  super(toCopy.getMySuperField());
  // ...
}

2
投票

不幸的是,没有内置的 JDK 可以不使用

NullPointerException
来执行此操作(尽管如果 NPE 可以使用自定义消息,则可以使用
Objects.requireNonNull
)。

但是,这可以通过

MySubClass
中的静态方法来完成,而不需要对
MySuperClass
进行任何修改。这与
Objects.requireNonNull
类似:

public class MySubClass extends MySuperClass {

    private static Object notNull(Object obj, Throwable onNull) throws Throwable {
        if (obj == null) throw onNull; // onNull cannot be null
        else return obj;
    }

    private final Object mySubField;
    public MySubClass(MySubClass toCopy) throws Throwable {
        super(
            notNull(
                toCopy,
                new AssertionError("Replace with something that makes sense")
           ).getMySuperField()
        );
        this.mySubField = toCopy.mySubField;
    }
}

这可以通过多种方式进行改进(例如对象和异常的泛型,避免

throws Throwable
),但这种实现应该是最低的共同点(并且应该适用于所有 JDK)。

编辑:为了方便起见,这里有一个更好的实现,具有泛型和更好的处理:

private static <T, E extends Exception> T notNull(T obj, E onErr) throws E {
    if (obj == null) throw Objects.requireNonNull(onErr, "Error must not be null");
    else return obj;
}
© www.soinside.com 2019 - 2024. All rights reserved.