2

ここに囲まれているインスタンスは、囲まれたインスタンスが有効な限りガベージコレクションの対象にはなりません。インスタンスを囲むための回避策はガベージコレクションの対象外です

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     return new Foo<>() { 
      T result = null; 

      boolean cached = false; 

      @Override 
      public T compute() { 
       if (!cached) { 
        result = Foo.this.compute(); 
        cached = true; 
       } 
       return result; 
      } 
     }; 
    } 
} 

これはその問題を解決していますか?

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     return Foo.memoize(this); 
    } 

    private static <T> Foo<T> memoize(Foo<T> original) { 
     class Bar implements Foo<T> { 
      T result = null; 

      Foo<T> foo; 

      Bar(Foo<T> original) { 
       foo = original; 
      } 

      @Override 
      public T compute() { 
       if (foo != null) { 
        result = foo.compute(); 
        foo = null; 
       } 
       return result; 
      } 
     } 
     return new Bar(original); 
    } 
} 
+0

これは[minimal](https://stackoverflow.com/help/mcve)ですか? – Michael

+1

@Michael私はそう信じています。 – gdejohn

答えて

1

最初のケースでは、匿名の内部クラスは、Foo.thisを使用して、囲むFooへの参照を「取得」します。匿名クラスはこの参照を無期限に保持するので、囲むFooは、匿名クラスも対象となるまでガベージコレクションの対象にはなりません。

2番目の例では、computeが一度呼び出された後、この参照は破棄されます。そのため、囲むクラスはそれ以降のガベージコレクションの対象となります。

は、ラムダ式として、ラムダ式を使用するための代替があることAccessing Members of an Enclosing Class

0

いいえ、なぜでしょうか?

memoize()関数内のBarインスタンスには、Fooオブジェクトを保持するフィールドがあります。スコープを使用するか暗黙のフィールド割り当てを使用するかは、ガベージコレクションとは無関係です。

ガベージコレクション可能なオブジェクトへの参照を保持する場合は、WeakReference/SoftReferenceを使用します。

+1

Barの 'foo = null'命令を見逃しました。 –

+0

瞬間にはなかったと誓ったでしょうか?)はい、あなたは正しいです。その場合、フィールドがヌルに設定された後、 'Foo'はガベージコレクションされます。静的内部クラスは、その点で外部クラスとまったく同じように動作します。 –

3

注を参照してください必要に応じ、例えばとしてのみキャプチャ値

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     AtomicReference<Foo<T>> holder = new AtomicReference<>(); 
     holder.set(() -> { 
       T result = compute(); 
       holder.set(() -> result); 
       return result; 
      } 
     ); 
     return() -> holder.get().compute(); 
    } 
} 

holderは、最初のラムダは、最初の評価にcomputeを起動するが、新しいラムダは、その常に意志Foo<T>を実施して、その後、それ自体を置換するにように、元のFoo<T>インスタンスへの参照を有するFoo<T>を導入含ま計算された値を返します。新しいラムダは、値が既に計算されているという事実が暗示されているので、条件付きを評価する必要さえない。

ここでホルダーオブジェクトはAtomicReferenceである必要はありませんが、標準的な使いやすい代替手段はありません。一般的な配列を作成することはできませんので、私の考えである唯一の選択肢はサイズ1の(変更可能な)リストです。 Arrays.asList(null)によって作成されます。

+0

私の神これは独創的です!私はこれをどのように呼び出すのか分からず、「変異する」ラムダは「自己変異する」ラムダですか?あまりにもスマートなwaaaay – Eugene

関連する問題