1

次のコードを再利用可能な関数でラップすることは可能ですか?

編集: これは単なる例です。すべての再帰の深さに対して有効なソリューションが必要です

私が欲しいのは、次のコードが生成されることです:

if (MyObject o == null || 
    o.getSubObject() == null ||
    o..getSubObject().getSubSubObject() == null /*|| 
    ... */)
    return defaultValue;
return o.getSubObject().getSubObject()/*...*/.getDesiredValue();

のようなものを呼び出すことによって

Object defaultValue = null;
Object result = NullSafeCall(o.getSubObject().getSubObject()/*...*/.getDesiredValue(), defaultValue);

2番目のコードブロックは単なるアイデアです。どのように見えるかは気にしません。必要に応じて、nullより深い関数を呼び出す前にすべてのチェックを回避できることだけが必要です...

インジェクションはおそらくこれを行うことができますが、他に/より簡単な解決策はありませんか? 注射はまだ見たことがありません...

EDIT2: 別の言語の例: http://groovy.codehaus.org/Operators#Operators-SafeNavigationOperator

4

5 に答える 5

1

Java8 は、適切なパフォーマンスで構文に最も近づけるのに役立ちます。

// Evaluate with default 5 if anything returns null.
int result = Optional.eval(5, o, x->x.getSubObject(), x->x.getDesiredValue());

これは、このユーティリティ クラスで実行できます。

class Optional {
    public static <T, Tdef, T1> Tdef eval(Tdef def, T input, Function<T,T1> fn1,
                                          Function<T1, Tdef> fn2)
    {
        if(input == null) return def;
        T1 res1 = fn1.apply(input);
        if(res1 == null) return def;
        return fn2.apply(res1);
    }
}

悲しいことに、チェーン内のメソッド呼び出しの数ごとに定義された個別の eval() が必要になるため、いくつか定義したい場合がありますが、コンパイル時の型は安全で、ほぼすべての呼び出し/型で再利用可能です。

于 2013-07-25T14:18:47.477 に答える
1

そうではありませんが、この方法で作成したコードは見栄えが悪く、リフレクションが非常に遅くなります。作成したコードを理解して変更できる実際の Java プリプロセッサを使用しない限り。

より良い (ただしかなりのリファクタリングに関連する) アプローチは、問題の値が null になる可能性がないことを確認することです。たとえば、個々のアクセサー ( getSubObject()getDesiredValue()) を変更して、最初から null を返さないようにすることができます。つまり、デフォルト値を返すようにします。デフォルト値のアクセサーは、デフォルト値を順番に返します。

于 2013-07-25T12:22:58.047 に答える
0

このようなことができます

public static Object  NullSafeCall(MyObject o,Object defaultValue){
    if ( o == null || o.getSubObject() == null)
    {
        return defaultValue;
    }
    else 
    {
        return o.getSubObject().getDesiredValue();
    }
}

これで、次のようにこのメソッドを呼び出すことができます

 Object result = NullSafeCall(o, defaultValue);
于 2013-07-25T12:24:46.650 に答える
0

私はちょうど交換することをお勧めします

Object result = NullSafeCall(o.getSubObject().getDesiredValue(), defaultValue);

によって

Object result = (o == null || o.subObject == null) ? defaultVlue : o.getSubObject().getDesiredValue();

再利用できる場合にのみメソッドを作成します......

于 2013-07-25T12:50:07.793 に答える
0

あなたが望むことは不可能です。この構文を使用することを理解することが不可欠です:Object result = NullSafeCall(o.getSubObject().getSubObject() ...);の部分は、コントロールが関数/メソッドに渡されるo.getSubObject().getSubObject()に評価されるため、例外がスローされます。

このようなコードを実行する前に、何らかのタイプのコンテキストが必要です。私が考えることができるこれに最も近いのは、以下の例のような匿名の内部クラスを使用して行うことができます。

// intended to be implemented by an anonymous inner class
interface NullSafeOperation<T> {
    public T executeSafely();
};

// our executor that executes operations safely
public static class NullSafeExecutor<T> {
    public NullSafeExecutor() {}

    public T execute(T defaultValue, NullSafeOperation<T> nso) {
        T result = defaultValue;
        try {
            result = nso.executeSafely();
        } catch(NullPointerException e) {
            // ignore
        }
        return result;
    }

    // utility method to create a new instance and execute in one step
    public static <T> T executeOperation(T defaultValue, NullSafeOperation<T> nso) {
        NullSafeExecutor<T> e = new NullSafeExecutor<T>();
        T result = e.execute(defaultValue, nso);
        return result;
    }
}


public static void main(String[] args) {

    final String aNullString = null;

    String result = NullSafeExecutor.executeOperation("MyDefault", new NullSafeOperation<String>() {
        @Override
        public String executeSafely() {
            // trying to call a method on a null string
            // it will throw NullPointerException but it will be catched by the executor
            return aNullString.trim(); 
        }
    });

    System.out.println("Output = " + result); // prints: Output = MyDefault
}
于 2013-07-25T13:29:30.433 に答える