web-dev-qa-db-ja.com

Java遅延評価がありますか?

この場合、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()は呼び出されますか?

26
m0skit0

Java(および他のCライクな言語)では、これは 短絡評価 と呼ばれます。*

はい、2番目の例ではisATrueが常に呼び出されます。つまり、コンパイラ/ JVMが観察可能な副作用がないと判断できない限り、その場合は最適化を選択できますが、その場合はとにかく違いに気付かないでしょう。


この2つはまったく異なります。前者は本質的に最適化手法ですが、2番目は言語によって義務付けられており、観測可能なプログラムの動作に影響を与える可能性があります。

私はもともとこれは遅延評価とはまったく異なることを提案しましたが、@ Ingoが以下のコメントで指摘しているように、それは疑わしい主張です。 Javaの遅延演算子の非常に限定的なアプリケーションとして、短絡演算子を見ることができます。

ただし、関数型言語が遅延評価のセマンティクスを要求する場合、通常は無限の(または少なくとも過剰な)再帰の防止というまったく異なる理由があります。

21

まあ、言語に関する限り-はい、両方の関数が呼び出されます。

関数をこれに書き直した場合:

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.
}
28
emesx

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の場合にのみ呼び出されます。この場合の唯一のペイロードは()->オンデマンドで遅延評価される値の前。

11
Dima

簡単にするために、次のようにJava 8のサプライヤーインターフェイスを使用できます。

Supplier<SomeVal> someValSupplier = () -> getSomeValLazily();

その後、後のコードで次のことができます:

if (iAmLazy) 
  someVal = someValSupplier.get(); // lazy getting the value
else 
  someVal = getSomeVal(); // non lazy getting the value
2
George

この質問スレッドで言及されているものに加えて追加したかったのは、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;
});
1
Abdullah Shahin

はい、isATrue()行で明示的に呼び出しているため、boolean a = isATrue();が呼び出されます。

ただし、isBTrue()trueを返す場合、次の場合には呼び出されません。

public boolean isTrue() {
    return isBTrue() || isATrue();
}
0
Vishal K

IsBTrue()がtrueを返す場合、isATrue()が呼び出されますか?

はい、両方とも呼ばれます。

0
NPE

いいえ、違います。 isBTrue()の結果に関係なく、isATrue()が呼び出されます。このようなプログラムを作成し、各メソッドにprintステートメントを使用して、これを自分で確認できます。

0
Jeremy Roman