2016-04-15 10 views
-1

私はFooとFooを実装するenum Barを持っています。次に、単純なビルダーパターンクラスのResultを使用しています。以下のコードを参照してください。Javaジェネリックは、型引数としてインタフェースを持ち、ビルダパターンを使用しています。

public interface Foo {} 

public enum Bar implements Foo { BAR } 

public class Result<T> { 
    public Result(T result) {} 

    public static <T> Result<T> of(T result) { 
     return new Result<>(result); 
    } 

    public Result<T> set() { 
     return this; 
    } 
} 

タイプBar.BARの検索結果を構築し、結果<はFoo >変数に代入しようとすると、これは正常に動作します:

Result<Foo> r1 = Result.of(Bar.BAR); 

をしかしfolowingは、コンパイルエラーが得られます。

Result<Foo> r2 = Result.of(Bar.BAR).set(); 

互換性のないタイプです。結果:<Foo>、結果:<バー>

誰でも説明してください。

+0

なぜ私は最初と2番目ではないが、なぜ結果を宣言しようとしているのか分かりません。 extends Foo> r2 = ... ' – Jason

+4

このコンパイルを行う最良の方法は' Result r2 = Resultです。 (Bar.BAR).set(); 'のです。 Java 8の型推論は大幅に改善されていますが、このようなメソッドを連結するときには、型パラメータを明示的に指定する必要があります。 –

答えて

3

Fooであると推測され、Barではないと推定された最初の例は、Java 8のターゲット型推論の結果としてコンパイルされます。

Result<Foo> r1 = Result.of(Bar.BAR); 

BarFooあるので、それはofメソッドに引数として渡すことができるので、それはコンパイルされます。

TBarFooではなく)と推定する必要があるため、2番目の例はコンパイルされません。代入演算子はr1に結果を割り当てる前

Result<Foo> r1 = Result.of(Bar.BAR).set(); 

set()メソッドが呼び出されます。ここで、Result.of(Bar.BAR).set()は、r1のタイプを考慮せずに、単独で考慮する必要があるため、TBarと推定されます。

はまた、Javaのジェネリックは不変であるので、BarResult<Bar>Result<Foo>ない、Foo場合でも。しかし、この状況を回避するためにワイルドカードを使うことができます。

Result<? extends Foo> r1 = Result.of(Bar.BAR).set(); 

最初の例は、もちろん別の回避策です。

Paul Boddingtonのコメントに記載されているもう1つの回避策は、汎用メソッドofへの明示的な型引数を使用することです。これにより、TFooが明示的に設定されます。

Result<Foo> r2 = Result.<Foo>of(Bar.BAR).set(); 

また、これはBuilder Patternではありません。 ofメソッドは単なるファクトリメソッドです。 Builderパターンは、目的クラスのインスタンスを構築することを目的とした別のクラスを使用します。

public class Result<T> { 
    // Prevent anyone except the Builder class from instantiating 
    // this class by making the constructor private. 
    private Result(T result) {} 

    public static class Builder<T> 
    { 
     private T result; 

     public void setResult(T result) 
     { 
      this.result = result; 
     } 

     public Result<T> build() 
     { 
      return new Result(result); 
     } 
    } 

    public Result<T> set() { 
     return this; 
    } 
} 
3

Could anyone please explain why?

ジェネリックは難しく、コンパイラはあなたが意味することを推測するのに苦労します。

これはJava 8の方が優れています。最初の試みはJava 7以降ではコンパイルできませんでした。

ofは一般的な方法です。型推論Javaでは、周囲のコンテキスト、この場合は割り当てられた変数の型を調べて、ターゲット型の引数を決定します。あなたの例では、Fooのサブタイプであるため、Bar.BARFooのサブタイプです。

2番目の例では、チェーン方法によって問題がより難しくなります。 setは一般的な方法ではありません。したがって、それは呼び出される式の型に依存します。メソッド連鎖のため、コンパイラはofの呼び出しのコンテキストに依存できません。それはそれが見ているようにタイプをとります。 Bar.BARBar。呼び出しは、次いで、set次いでBarの戻り型を有する

((Result<Bar>) Bar.BAR).set(); 

(タイプを明確にする)となります。

そしてので

Result<Bar>ので一つの式が他のに割り当てることではありませんResult<Foo>ではありません。

関連する問題