2016-12-28 19 views
1

現在、グリッドのような構造の操作を処理できる抽象コードで作業しています。具体的なメソッドの代わりに、ラムダ式を受け取り、グリッド要素間の方向性関係に基づいて機能を提供するフレームワークを開発しようとしています。これらのメソッドにジェネリック型を返そうとすると問題に遭遇しました。一般的な戻り値の型とラムダ関数の引数

私は、特定の方向のグリッド要素を見つけるラムダ関数と各要素のいくつかのメソッドを実行するコンシューマを受け入れる以下のような作業メソッドを持っています。代わりに、このメソッドが呼び出されるたびに、要素発見機能を渡しするので、私はそのような機能が宣言され、にforEachClockwiseメソッドをオーバーロードしているインタフェースの直交」を持っていいだろうことを決めた

public static <E> void forEachClockwise(Direction d, Function<Direction, ? extends E> f, Consumer<? super E> c){ 
    /*For each of the orthogonal directions, in order:*/ 
     c.accept(f.apply(d)); 
    /*end For*/ 

} 

このインタフェースの要素を受け入れます。

public static <O extends Orthogonal> void forEachClockwise(Direction d, O center, Consumer<O> c){ 
    forEachClockwise(d, center::findNextByDirection, c); 
} 

私はここでジェネリックを使用し、Consumerメソッドがキャストせずにグリッド要素のフィールドにアクセスできるように宣言しました。私はこれが設計の選択肢が貧弱であることを心配しています。私が作成した直交インターフェースとテストクラスは、いくつかの異なることを試しても警告なしでキャストなしでコンパイルされていないためです。私が試したfindNextByDirectionの

public interface Orthogonal { 
    public <E extends Orthogonal> E findNextByDirection(Direction a); 
} 

2 '有効' の実装は以下のとおりです。

/* in Coordinate implements Orthogonal: */ 
@Override 
public <E extends Orthogonal> E findNextByDirection(Direction a) { 
    return (E) (/*The proper Coordinate*/); 
} 

そして

最適
@Override 
public Coordinate findNextByDirection(Direction a) { 
    return /*The proper Coordinate*/; 
} 
//Warning on return type declaration: 
/*Type safety: The return type Element for findNextByDirection(Direction) from the type Element needs unchecked conversion to conform to E from the type Orthogonal*/ 

、私はforEachClockwise方法を持っていると思います(と他のメソッド)が受け入れることができます。これは、キャストせずに、それを呼び出す同じクラスの要素を返します。座標のfindNextByDirectionは座標を返し、Addressはアドレスを返し、CellはCellを返します。その他の質問と回答ここでは、一般的な戻り値の型を持つメソッドを作る方法を説明しますが、見つけませんでしたこの種のメソッドをラムダパラメータとして使用する方法に関するヒント。

私がまだ試していないことは、いくつかの種類の直交性と方向性を引数として受け入れる新しいfindByDirectionメソッドを作成することです。あるいは、ForEachClockwiseメソッドをOrthogonalで定義されたいくつかのメソッドで呼び出すことです。私が試みたことのように。他のアプローチは、インターフェイスに独自のジェネリック型を与えることですが、明らかに間違っていて、すべての直交クラスを "MyClassが直交を実装する"と宣言するのは妥当ではありません。

このインターフェイスを完全に設計しましたかジェネリックが彼らが設計されていないことをやろうとしていることを期待していますか?または、クラスを取得して正しいメソッドを呼び出し、それ自体の型の要素を総称的に返す簡単な方法がありますか?

全コード:

package test; 

import java.util.function.Consumer; 
import java.util.function.Function; 


public enum Direction { 
    NORTH, EAST, SOUTH, WEST; 

    public static <O extends Orthogonal> void forEachClockwise(Direction d, O center, Consumer<O> c){ 
     forEachClockwise(d, center::findNextByDirection, c); 
    } 

    public static <X> void forEachClockwise(Direction d, Function<Direction, ? extends X> f, Consumer<? super X> c){ 
     c.accept(f.apply(d)); 
     forEachExcept(d, f, c); 
    } 

    public static <X> void forEachExcept(Direction d, Function<Direction, ? extends X> f, Consumer<? super X> c){ 
     for(int i=0; i<3; i++){ 
      d = Direction.getClockwise(d); 
      c.accept(f.apply(d)); 
     } 
    } 

    public static Direction getClockwise(Direction d){ 
     switch(d){ 
     case EAST: 
      return SOUTH; 
     case NORTH: 
      return EAST; 
     case SOUTH: 
      return WEST; 
     case WEST: 
      return NORTH; 
     default: 
      return null; 
     } 
    } 

} 

interface Orthogonal { 
    public <E extends Orthogonal> E findNextByDirection(Direction a); 
} 






class Coordinate implements Orthogonal{ 

    int x; 
    int y; 

    public Coordinate(int x, int y){ 
     this.x = x; 
     this.y = y; 
    } 

    public int getX(){ 
     return x; 
    } 

    public int getY() { 
     return y; 
    } 


    @Override //Warning on "Coordinate" below. Compiler suggests writing 
       //entire <E extends Orthogonal> method signature 
    public Coordinate findNextByDirection(Direction a) { 
     switch(a){ 
      case NORTH: 
       return new Coordinate(x+1, y); 
      case SOUTH: 
       return new Coordinate(x-1, y); 
      case EAST: 
       return new Coordinate(x, y+1); 
      case WEST: 
       return new Coordinate(x, y-1); 
      default: 
       return null; 
     } 
    } 
} 
+0

(警告付きで)コンパイルする完全な例を作成できれば役に立ちます。例えば、この実装は警告なしでコンパイルします。 'public class Elementは直交{ @Overrideを実装します。public Element findNextByDirection(Direction a){ これを返します。要素からEに準拠するように未チェックの変換を必要とするタイプからfindNextByDirectionの戻り値の型の要素(ディレクション): 」型の安全性:スニペット } } ' – assylias

+0

は私、戻り値の型宣言要素のための警告でコンパイルしませんOrthogonal型の " –

+0

"を返しましたか? – assylias

答えて

0

私の理解では、あなたのfindNextByDirection方法から、あなたは、囲むクラスと同じ型を返す必要があるということです。したがって、Coordinateの例では、Coordinateを返す必要があります。その場合は

、あなたはジェネリックでさらに一歩行くことができます:

interface Orthogonal<E extends Orthogonal<E>> { 
    public E findNextByDirection(Direction a); 
} 
class Coordinate implements Orthogonal<Coordinate> { 
    //rest is the same 
    //you should not have any warnings left 
} 

public static <O extends Orthogonal<O>> void forEachClockwise2(Direction d, O center, Consumer<O> c){ 
    forEachClockwise(d, center::findNextByDirection, c); 
} 

注:2つのforEachClockwiseの方法は、競合(同じ生署名を)持っているので、私はforEachClockwise2にそれらのいずれかの名前を変更しました。

2

Orthogonal-CoordinateAreaの2つのクラスがあります。

public <E extends Orthogonal> E findNextByDirection(Direction a); 

あなたがここで宣言したものはテンプレート化された方法です。宣言は基本的に次のようになります。「特定のコンテキストを指定すると、このメソッドは常に期待される型を返します。結果をCoordinate変数に代入する場合、メソッドはように - 限り、どのような期待されていることがOrthogonal

を実装タイプであるとして、あなたはpublic Coordinate findNextByDirection(Direction a)としてCoordinateでそれを実装しようとしたとき、あなたはもはや指定されたコンテキストで期待されているものは何でも返すことを約束する - あなたの今だけこれまで通りCoordinateを返し、インタフェースで宣言された契約を破った。さらに悪いことに、あなたがそれを考えるなら、最初にその契約を実際に満足させる方法はありませんでした。

インターフェイスの汎用(Orthogonal<E>)を宣言し、<E extends Orthogonal>をメソッド宣言から削除します。

class Coordinate implements Orthogonal<Coordinate>が「はっきりと間違っている」と宣言されている場合は、そうではありません。これは一般的な方法であり、SDK自体で広く使用されています(たとえば、Comparableを参照)。

findNextByDirectionOrthogonalを返す必要がある場合は、返品タイプをOrthogonal<E>と宣言できます。 Coordinateのメソッドをpublic Coordinate findNextByDirection(Direction a)として実装することは、Javaの型共分散のために警告を表示することなく実行する必要があります。

+0

"それでも思えば、最初はその契約を実際に満足させる方法がありませんでした。"常に 'null'を返さない限り:) – newacct

関連する問題