この場合、Javaにはスマート/レイジー評価があります:
_public boolean isTrue() {
boolean a = false;
boolean b = true;
return b || (a && b); // (a && b) is not evaluated since b is true
}
_
しかし、どうですか:
_public boolean isTrue() {
boolean a = isATrue();
boolean b = isBTrue();
return b || a;
}
_
isATrue()
がtrueを返した場合でも、isBTrue()
は呼び出されますか?
Java(および他のCライクな言語)では、これは 短絡評価 と呼ばれます。*
はい、2番目の例ではisATrue
が常に呼び出されます。つまり、コンパイラ/ JVMが観察可能な副作用がないと判断できない限り、その場合は最適化を選択できますが、その場合はとにかく違いに気付かないでしょう。
私はもともとこれは遅延評価とはまったく異なることを提案しましたが、@ Ingoが以下のコメントで指摘しているように、それは疑わしい主張です。 Javaの遅延演算子の非常に限定的なアプリケーションとして、短絡演算子を見ることができます。
ただし、関数型言語が遅延評価のセマンティクスを要求する場合、通常は無限の(または少なくとも過剰な)再帰の防止というまったく異なる理由があります。
まあ、言語に関する限り-はい、両方の関数が呼び出されます。
関数をこれに書き直した場合:
public boolean isTrue() {
return isBTrue() || isATrue();
}
最初の関数がtrueの場合、2番目の関数は呼び出されません。
しかし、これは遅延評価ではなく短絡評価です。遅延評価の場合は次のようになります。
public interface LazyBoolean {
boolean eval();
}
class CostlyComparison implements LazyBoolean {
private int a, b;
public CostlyComparison(int a, int b) {
this.a=a;
this.b=b;
}
@Override
public boolean eval() {
//lots of probably not-always-necessary computation here
return a > b;
}
}
public LazyBoolean isATrue() {
return new CostlyComparison(10,30); //just an example
}
public boolean isTrue() { // so now we only pay for creation of 2 objects
LazyBoolean a = isATrue(); // but the computation is not performed;
LazyBoolean b = isBTrue(); // instead, it's encapsulated in a LazyBoolean
return b.eval() || a.eval(); // and will be evaluated on demand;
// this is the definition of lazy eval.
}
SE8(JDK1.8)は Lambda式 を導入しました。これにより、遅延評価をより透明にすることができます。次のコードのmainメソッドのステートメントを検討してください。
@FunctionalInterface
public interface Lazy<T> {
T value();
}
class Test {
private String veryLongMethod() {
//Very long computation
return "";
}
public static <T> T coalesce(T primary, Lazy<T> secondary) {
return primary != null? primary : secondary.value();
}
public static void main(String[] argv) {
String result = coalesce(argv[0], ()->veryLongMethod());
}
}
呼び出された関数は、最初に指定されたnull以外の値を返します(SQLの場合と同様)。呼び出しの2番目のパラメーターはLambda式です。メソッドveryLongMethod()は、argv [0] == nullの場合にのみ呼び出されます。この場合の唯一のペイロードは()->
オンデマンドで遅延評価される値の前。
簡単にするために、次のようにJava 8のサプライヤーインターフェイスを使用できます。
Supplier<SomeVal> someValSupplier = () -> getSomeValLazily();
その後、後のコードで次のことができます:
if (iAmLazy)
someVal = someValSupplier.get(); // lazy getting the value
else
someVal = getSomeVal(); // non lazy getting the value
この質問スレッドで言及されているものに加えて追加したかったのは、JVMのOracleドキュメントからです
a Java仮想マシンの実装は、使用時にクラスまたはインターフェースの各シンボリック参照を個別に解決するか(「遅延」または「遅延」解決)、またはクラスが検証されている(「熱心な」または「静的な」解決)これは、一部の実装では、クラスまたはインターフェースが初期化された後、解決プロセスが継続する可能性があることを意味します。
遅延実装を持つクラスの例がStreamであるため、これはStreamのOracleドキュメントからです。
ストリームは遅延しています。ソースデータの計算は端末操作が開始されたときにのみ実行され、ソース要素は必要な場合にのみ消費されます。
そうは言っても、次の操作を行うと、何も表示されません。イニシエーターを追加しない限り。
Steam.of(1, 2, 3, 4, 5).filter(number -> {
System.out.println("This is not going to be logged");
return true;
});
はい、isATrue()
行で明示的に呼び出しているため、boolean a = isATrue();
が呼び出されます。
ただし、isBTrue()
がtrue
を返す場合、次の場合には呼び出されません。
public boolean isTrue() {
return isBTrue() || isATrue();
}
IsBTrue()がtrueを返す場合、isATrue()が呼び出されますか?
はい、両方とも呼ばれます。
いいえ、違います。 isBTrue()
の結果に関係なく、isATrue()
が呼び出されます。このようなプログラムを作成し、各メソッドにprintステートメントを使用して、これを自分で確認できます。