Java 泛型返回类型和lambda函数参数

Java 泛型返回类型和lambda函数参数,java,generics,lambda,Java,Generics,Lambda,我目前正在研究抽象代码,它可以处理任何类似网格的结构上的操作。作为具体方法的替代,我一直在尝试开发一个框架,该框架接受lambda表达式,并基于网格元素之间的方向关系提供功能,而我在尝试向这些方法返回泛型类型时遇到了麻烦 我有一个类似于下面的工作方法,它接受一个lambda函数,该函数在特定方向上查找网格元素,并接受一个消费者,该消费者在每个元素上执行某种方法 public static <E> void forEachClockwise(Direction d, Function&

我目前正在研究抽象代码,它可以处理任何类似网格的结构上的操作。作为具体方法的替代,我一直在尝试开发一个框架,该框架接受lambda表达式,并基于网格元素之间的方向关系提供功能,而我在尝试向这些方法返回泛型类型时遇到了麻烦

我有一个类似于下面的工作方法,它接受一个lambda函数,该函数在特定方向上查找网格元素,并接受一个消费者,该消费者在每个元素上执行某种方法

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

}
最理想的情况是,我希望有一个ForEach顺时针(和其他方法)可以接受的方法,该方法返回调用它的同一类的元素,而无需强制转换。坐标的findNextByDirection应该返回坐标,Address应该返回地址,Cell应该返回Cell,等等。我看到的其他问题和答案讨论了如何使用泛型返回类型创建方法,但我没有找到任何关于如何将这种方法用作lambda参数的提示

有几件事我还没有尝试过,就是制作一个新的findByDirection方法,它接受某种正交和一个方向作为参数,或者用正交中定义的方法调用foreach顺时针方法,尽管这些方法听起来和我尝试的方法一样合理。我见过的另一种有效的方法是为接口提供自己的泛型类型,但将每个正交类声明为“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;
        }
    }
}
封装测试;
导入java.util.function.Consumer;
导入java.util.function.function;
公共枚举方向{
北、东、南、西;
顺时针方向(方向d,O中心,耗电元件c){
Foreach顺时针方向(d,中心::findNextByDirection,c);
}

公共静态void foreach顺时针(方向d,函数f,使用者我的理解是,从
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);
}
界面正交{
公共E findNextByDirection(方向a);
}
类坐标实现正交{
//其余的都一样
//您不应该留下任何警告
}
公共静态无效ForEachWise2(方向d,O中心,耗电元件c){
Foreach顺时针方向(d,中心::findNextByDirection,c);
}

注意:这两个
foreach顺时针
方法有冲突(相同的原始签名),因此我将其中一个重命名为
forEachClockwise2

让我们假设有两个类实现了
正交
-
坐标
区域

public <E extends Orthogonal> E findNextByDirection(Direction a);
public E findNextByDirection(方向a);
您在这里声明的是一个模板化方法。声明基本上说:"给定特定的上下文,此方法将始终返回预期的类型-如果将结果分配给
坐标
变量,则该方法将返回
坐标
,如果返回
区域
,则将返回
区域
,依此类推-只要预期的是实现
正交的类型


但是,当您尝试在
坐标中实现它时,作为
公共坐标findNextByDirection(方向a)
,您不再承诺返回给定上下文中所期望的任何内容-您现在只返回
坐标
,从而破坏了界面中声明的契约。更糟糕的是,如果您想一想,根本无法实际满足该契约

您应该做的是将接口声明为泛型(
Orthogonal
),并从方法声明中删除

如果声明
类坐标实现了正交
“明显错误”,则不应该这样做。这是常见的做法,在SDK本身中广泛使用(例如,请参见
Comparable


如果您发现有必要使用
findNextByDirection
返回一个
Orthogonal
,您可以将返回类型声明为
Orthogonal
。您甚至可以在
坐标
中作为
公共坐标findNextByDirection(方向a)实现您的方法
由于Java的类型协变性而没有任何警告。

如果您可以生成一个编译(带有警告)的完整示例,这会有所帮助。例如,这种没有警告的正交编译的实现:
公共类元素实现正交{@Override public Element findNextByDirection(方向a){return this;}
该代码段编译时在返回类型声明元素上给我一个警告:“类型安全:类型元素中findNextByDirection(Direction)的返回类型元素需要未经检查的转换以符合类型或对角的E”是否复制了方法签名(返回一个元素,而不是正交的)?是的。我正在使用Eclipse进行编译。从其他问题来看,使用Eclipse和javac在使用传递泛型时可能会抛出不同的编译器警告和错误。“更糟糕的是,如果你想一想,一开始就没有办法真正满足该契约。“除非它总是返回
null
:)
@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*/
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;
        }
    }
}
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);
}
public <E extends Orthogonal> E findNextByDirection(Direction a);