発生元情報を保持するnullっぽい何か
Informative Null Pointer (1) - d.y.dで仰ってるのは、こんな感じでしょうか。確かに、あったら便利そう。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class Nil { /** * 指定したインタフェースクラスのNilオブジェクトを生成、返却する。 * 生成したNilオブジェクトは、全てのメソッド呼び出しに対して、 * Nilオブジェクト生成時のスタックトレースを保持した{@link NullPointerException}をスローする。 * 指定したクラスがインタフェースではない場合、{@link IllegalArgumentException}をスローする。 * @param <T> 指定したNilオブジェクトのクラスと同一のクラス * @param clazz 作成するNilオブジェクトのインタフェースクラス * @return 指定したインタフェースクラスのNullオブジェクト * @throws IllegalArgumentException 指定したクラスがインタフェースではない場合 */ @SuppressWarnings("unchecked") public static <T> T nil(final Class<T> clazz) { if (!clazz.isInterface()) throw new IllegalArgumentException("class must be interface."); Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz); try { return (T)proxyClass.getConstructor(InvocationHandler.class).newInstance( new InvocationHandler() { private final NilPointer e = removeInternalStackTraceElements( new NilPointer()); @Override public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable { NullPointerException nulpo = removeInternalStackTraceElements( new NullPointerException()); nulpo.initCause(e); throw nulpo; } } ); } catch (Exception e) { throw new RuntimeException(e); } } /** * スタックトレースの内、内部情報として持つ先頭2要素を削除して返却する。 */ private static <T extends Throwable> T removeInternalStackTraceElements(T t) { t.setStackTrace( Arrays.asList(t.getStackTrace()) .subList(2, t.getStackTrace().length) .toArray(new StackTraceElement[0])); return t; } /** * Nilオブジェクト生成時のスタック情報を管理するクラス。 */ private static class NilPointer extends NullPointerException {} }
public class NilTest { public static Runnable usingNil() { return Nil.nil(Runnable.class); } public static Runnable usingNull() { return null; } public static void main(String[] args) { try { Runnable r = NilTest.usingNull(); r.run(); } catch (Exception e) { e.printStackTrace(); } try { Runnable r = NilTest.usingNil(); r.run(); } catch (Exception e) { e.printStackTrace(); } } }
usingNull: java.lang.NullPointerException at NilTest.main(NilTest.java:12) usingNil: java.lang.NullPointerException at NilTest.main(NilTest.java:19) Caused by: Nil$NilPointer at NilTest.usingNil(NilTest.java:4) at NilTest.main(NilTest.java:18)
他、ダミーオブジェクト生成のライブラリとかもあるから、見様見真似で作れば、「生成するのはインタフェースのみ」なんて嫌な制約も減らせるはず。
とは言え、これってJavaのVMとか言語レベルで解決してくれない限り、結局APIのほとんどをラップしなきゃいけないことには変わりないわけで、結局「ぬるぽ可能性を緩やかに許容する」のが現実的でしょうね。