2016-12-28 99 views
1

我目前正在處理可以處理任何網格狀結構上的操作的抽象代碼。代替具體的方法,我一直在嘗試開發一個接受lambda表達式的框架,並基於網格元素之間的方向關係提供功能,並且在試圖將泛型類型返回到這些方法時遇到了麻煩。通用返回類型和lambda函數參數

我有一個像下面這樣的工作方法,它接受一個lambda函數,它在特定方向上查找網格元素,以及一個Consumer在每個元素上執行一些方法。

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*/ 

} 

而不必每此方法被調用時通過一個元件調查功能的,我決定這將是最好有具有聲明這樣的功能的接口「正交」,和重載forEachClockwise方法接受這個接口的元素。

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

我在這裏使用了泛型,並聲明瞭消費者方法可以在不投射的情況下訪問網格元素的字段。我擔心這是一個糟糕的設計選擇,因爲儘管嘗試了一些不同的東西,正交界面和我創建的測試類沒有編譯沒有警告和沒有強制轉換。

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

兩個 '有效' findNextByDirection。我試圖實現是:

/* 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應該返回一個座標,地址應該返回一個地址,單元格應該返回單元格等。我看過的其他問題和答案討論如何脫離使用泛型返回類型的方法,但我還沒有找到關於如何使用這種方法作爲lambda參數的任何提示。

我還沒有試過的一些東西正在創建一個新的findByDirection方法,它接受某種正交和方向作爲參數,或者用正交中定義的某種方法調用forEachClockwise方法,儘管這些方法聽起來像是合理的正如我所嘗試的那樣。我見過的另一種方法是爲接口提供它自己的泛型類型,但是它感覺明顯錯誤並且意圖破壞將每個正交類聲明爲「MyClass implements Orthogonal」

我是否已經完全設計了此接口從一開始就錯誤了,並希望仿製藥做一些他們沒有設計的東西?還是有一種簡單的方法讓一個類調用正確的方法並一般返回它自己類型的元素?

全碼:

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

這將幫助,如果你能產生一個完整的例子,編譯(含警告)。例如,這種實現正交的編譯沒有警告:'公共類Element實現正交{ @覆蓋公共元素findNextByDirection(A方向){ 回報這一點; } }' – assylias

+0

該代碼段在我的返回類型聲明中編譯時發出警告聲明元素: 「類型安全性:類型元素的findNextByDirection(Direction)元素需要未經檢查的轉換以符合E從類型Orthagonal「 –

+0

你複製了方法簽名(返回一個元素,而不是正交)? – assylias

回答

0

我的理解是,從你的findNextByDirection方法,你需要返還相同種類的封閉類。所以在你的座標示例中,你需要返回一個座標。

如果是這樣的話,你可以走一步與仿製藥:

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); 
} 

注:兩個forEachClockwise方法有衝突(同一原始簽名),所以我改名爲其中之一forEachClockwise2

2

讓我們假設有兩個類實現Orthogonal-CoordinateArea

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

你在這裏聲明的是一種模板化方法。聲明基本上說:「給一個特定的背景下,此方法將總是返回預期的類型 - 如果結果是被分配到一個Coordinate變量,該方法將返回一個Coordinate,如果一個Area - 它會返回一個Area和等等 - 只要有什麼預期是實現Orthogonal

但是,當你嘗試實現它Coordinatepublic Coordinate findNextByDirection(Direction a)一個類型,你不再答應返回任何預計在特定情況下 - 你現在只永遠返回Coordinate,從而打破界面中聲明的合同。更糟糕的是,如果你想到它,首先就沒有辦法真正滿足那份合同。

你應該做的是聲明你的接口通用(Orthogonal<E>)並從方法聲明中刪除<E extends Orthogonal>

如果是感覺「明顯錯誤」的宣言class Coordinate implements Orthogonal<Coordinate>,它不應該。這是常見的做法,廣泛用於SDK本身(例如參見Comparable)。

如果您發現findNextByDirection需要返回Orthogonal,您可以聲明返回類型爲Orthogonal<E>。由於Java類型的協方差,您甚至應該能夠在Coordinate中以public Coordinate findNextByDirection(Direction a)的方式實現您的方法,而不會有任何警告。

+0

「更糟糕的是,如果你想到它,首先就沒有辦法真正滿足合同。」除非它總是返回'null' :) – newacct