2016-12-28 77 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