I am using Java 21.
I have two classes:
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;
}
}
The class MySubClass
has a copy constructor, as shown above.
I want to avoid throwing a NullPointerException
in the MySubClass
constructor if the toCopy
argument is null, and would rather throw my own exception. But of course, the call to super(...)
must be the first line in the constructor.
Is there some Java pattern which I can use to do my own argument validation before calling super(...)
?
I have tried a few different things, but they all seem ugly/hacky, like passing nulls into the super class constructor and not doing validation in the super class, and then doing the argument validation after the call to super(...)
in the subclass, or making mySuperField
in MySuperClass
not final, and providing a setter in the super class. There must be something better.
Of everything I have tried, this might be the best option, but it still feels hacky.
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;
}
}
Unfortunately, there is no JDK builtin to do this that doesn't use a NullPointerException
(though if an NPE would work with a custom message, you can use Objects.requireNonNull
).
However, this could be accomplished with a static method in MySubClass
, without any modifications to MySuperClass
. This works similarly to 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;
}
}
This could be improved in a couple ways (e.g. generics for object and exception, avoiding throws Throwable
) but this implementation should be the lowest common denominator (and should work on all JDKs).
Edit: For convenience, here's a better implementation with generics and better handling:
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;
}