Java 中的错误消息(流和 Lambda 理解)

Error Message in Java (Stream and Lambda Comprehension)

我在尝试调试我的代码时被困了几个小时。

这是我收到的错误消息

Error:(8, 8) java: trump.Wall is not abstract and does not override abstract method get() in java.util.function.Supplier

这些是与该错误对应的 classes。所以当我 运行 class DonalTrump 它给了我上面的错误信息。显然是因为 Wall class。以下是我的代码

DonaldTrump

package trump;

import java.util.*;
import java.util.stream.*;
import java.util.function.BiConsumer;

public class DonaldTrump{

    public static void main(String[] args) {
        if (args.length < 3) {
            System.out.println("Need three integer arguments: width height #bricks");
            System.exit(1);
        }
        int width = Integer.parseInt(args[0]);
        int height = Integer.parseInt(args[1]);
        int numberOfBricks = Integer.parseInt(args[2]);
        assert numberOfBricks <= width * height: "Too many bricks";
        System.out.printf("Will build a wall %d wide and %d tall%n",
                width, height);
        System.out.println(String.join("", Collections.nCopies(width,"==")));

        Wall trumpWall
        = Stream.generate(() -> new Ball(10.0))
                .filter(b -> b.colour == Ball.Colour.RED)
                .map(Brick::new)
                .limit(numberOfBricks)
                .collect(() -> new Wall(width, height), Wall::accept, Wall::combine); //UPDATE
        System.out.println(trumpWall);
    }
}

//Wall SOURCE OF ERROR HERE

package trump;

import java.util.*;
import java.util.function.Supplier;
import java.util.stream.*;


public class Wall implements Supplier<Wall> { //ERROR HERE//

    public Wall get() { //UPDATE
    return this;
    }

    private Brick[][] bricks;
    private int width;
    private int height;
    private int lastFilledX;
    private int lastFilledY;
    private boolean isComplete;

    final String sideBrick = "\u2b1b";
    final String innerBrick = " \u2b1b"; // black box (brick) in Unicode

    public Wall(int w, int h) {
        assert w > 0 && h > 0 : "the wall must have finite width and height";
        this.width = w;
        this.height = h;
        this.bricks = new Brick[width][height];
        for (int j = 0; j < this.height; j++)
            for (int i = 0; i < this.width; i++)
                this.bricks[i][j] = null;
        this.lastFilledX = 0;
        this.lastFilledY = 0;
        // this.isComplete = false;
    }

    public void lay(Brick brick) {
        if (this.isComplete()) return;
        this.bricks[lastFilledX][lastFilledY] = brick;
        // if (this.isComplete()) return false;
        if (this.lastFilledX == this.width - 1) {
            this.lastFilledX = 0;
            this.lastFilledY += 1;
        } else {
            this.lastFilledX += 1;
        }
    }

    public boolean isComplete() {
        return Stream.of(this.bricks).allMatch(
                level -> Stream.of(level).allMatch(b -> b != null));
        // return (this.lastFilledX == this.width - 1 &&
        //      this.lastFilledY == this.height - 1);
    }

    @Override
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        for (int j = this.height - 1; j >= 0; j--) {
            for (int i = 0; i < this.width; i++)
                // try any from the range u25a2 -- u25a9
                buffer.append(bricks[i][j] == null ? "   " :
                        i == 0 ? sideBrick : innerBrick);
            // buffer.append(bricks[i][j] == 0 ? "0" : "1");
            buffer.append("\n");
        }
        // return "3[31m" + buffer.toString() + "3[0m";
        return buffer.toString(); // to hell with color code sequence
    }

    public static Wall linkTwoWalls(Wall w1, Wall w2) {
        assert w1.height == w2.height : "Walls have unequal height";
        if (!w1.isComplete() || !w2.isComplete())
            return null; //Optional.empty();
        int w = w1.width + w2.width;
        int h = w1.height;
        Brick[][] bricks = new Brick[w][h];
        System.arraycopy(w1, 0, bricks, 0, w1.width);
        System.arraycopy(w2, w1.width, bricks, 0, w2.width);
        Wall result = new Wall(w, h);
        result.bricks = bricks;
        return result;//Optional.of(result);
    }

    public static Optional<Wall> joinWalls(Wall... walls) {
        if (walls == null || walls.length == 0)
            return Optional.empty();
        // check all walls are of the same height
        int firstHeight = walls[0].height;
        Stream<Wall> wallStream = Stream.of(walls);
        assert wallStream.allMatch(w -> w.height == firstHeight);
        return wallStream.reduce((w1, w2) -> linkTwoWalls(w1, w2));
    }

    public void accept(Wall wall, Brick brick) { //UPDATE NOT STATIC
        wall.lay(brick);
    }

    public void combine(Wall w1, Wall w2) { //UPDATE NOT STATIC
        Wall.linkTwoWalls(w1, w2);
    }


    public static void main(String[] args) {
        Wall wall = new Wall(40, 10);
        for (int i = 0; i < 411; i++) {
            wall.lay(new Brick(new Ball(10.0)));
            if (wall.isComplete())
                break;
        }
        System.out.print(wall);
    }
}

您需要覆盖 Supplier 接口的 get 方法。

public Wall get(){
      return this;
}

乍一看,我发现您的代码有 2 个问题:

  1. 您没有在 DonaldTrump class 中调用 collect 时指定 Supplier<Wall>,或者您做错了。让 Wall class 实现 Supplier<Wall> 接口是不正确的。从现实生活的角度来想一想:墙是自身的供应者是没有意义的。相反,您应该使用与 Supplier 接口匹配的 lambda 表达式,即 就像您正在实施 Supplier.get 方法一样工作的表达式。这是() -> new Wall(width, height).

  2. 在您的 Wall class 中,acceptcombine 方法都不应该是静态的。此外,accept 不应接收 Wall 的实例,而应仅接收一个 Brick,并将其放入此 Wall 中。此外,combine 方法应该只接受一个 Wall 参数并将此参数与此 Wall 组合。也许您可以阅读 lesson about method references in The Java Tutorial,它清楚地解释了所有不同的方法引用类型以及何时使用它们。

考虑到这些因素意味着您应该对代码进行一些更改。

在您的 DonaldTrump class 中,将 () -> new Wall(width, height) lambda 表达式作为 collect 方法的 Supplier

Wall trumpWall = Stream.generate(() -> new Ball(10.0))
    .filter(b -> b.colour == Ball.Colour.RED)
    .map(Brick::new)
    .limit(numberOfBricks)
    .collect(() -> new Wall(width, height), Wall::accept, Wall::combine);

然后在您的 Wall class 中,按如下方式更改 acceptcombine 方法:

public void accept(Brick brick) { // Lay a brick into THIS wall
    this.lay(brick);
}

public void combine(Wall wanother) { // Combine another wall with THIS wall
    this.linkToThisWall(another);
}

其中 linkToThisWall 将是您的(现在无用的)linkTwoWalls 方法的修改版本:

public void linkToThisWall(Wall another) {
    assert this.height == another.height : "Walls have unequal height";
    if (!this.isComplete() || !another.isComplete()) {
        return; // or maybe throw an exception?
    }        
    int w = this.width + another.width;
    int h = this.height;
    Brick[][] newBricks = new Brick[w][h];
    System.arraycopy(this.bricks, 0, newBricks, 0, this.width);
    System.arraycopy(another.bricks, this.width, bricks, 0, another.width);
    this.bricks = newBricks;
}

同时考虑删除 get 方法,因为不再需要实施 Supplier<Wall>

实际上,通过此代码修复和重构,您不再需要 acceptcombine 方法。在您的 DonaldTrump class 中,您可以只使用对重构的 laylinkToThisWall 方法的引用:

Wall trumpWall = Stream.generate(() -> new Ball(10.0))
    .filter(b -> b.colour == Ball.Colour.RED)
    .map(Brick::new)
    .limit(numberOfBricks)
    .collect(() -> new Wall(width, height), Wall::lay, Wall::linkToThisWall);

编辑: 这些更改的主要原因是您没有正确使用 Stream.collect 方法。

Stream.collect 需要 3 个参数:

  1. 将用于创建累积的供应商,可变 结构,用于累积流的元素。在您的代码中,此结构是 Wall,流的元素是 Brick 的实例,因此供应商是 () -> new Wall(width, height)。该供应商可能被视为一堵空墙,即就像地面上开始砌砖的地方。
  2. 一个累加器,它是一个接受两个参数的 BiConsumer:前一个项目的供应商返回的结构和流的一个元素。这个累加器 biconsumer 的契约是它必须将流的一个元素累加到累积的可变结构中。在您的例子中,累积的可变结构是由上面的供应商创建的 Wall,流的元素是 Brick 的实例,因此累加器是 Wall::lay,或者使用 lambda (wall, brick) -> wall.lay(brick)。这个蓄能器可以看作是一个工人把砖块一块一块地砌成墙。
  3. 一个组合器,它是一个接受两个参数的 BiConsumer,这两个参数都是部分填充的可变结构的实例(这些结构与 Supplier 提供的结构具有相同的类型第 1 项)。当最终结构的创建可以并行化时使用此组合器,其契约是将第二个参数结构组合(或合并,或混合,或 link 或连接)到第一个参数结构中。在您的例子中,部分填充的可变结构是两个 Wall 实例,部分填充了砖块,因此组合器是 Wall::linkToThisWall,或使用 lambda (leftWall, rightWall) -> leftWall.linkToThisWall(rightWall)。所有这些组合的东西都可以看作是两个独立的工人并行工作,每个工人都在自己的墙上砌砖:一个工人从左边开始,另一个从右边开始;当它们在中间相遇时,两个半墙组合成一个新的完整墙。

至于为什么你的解决方案不正确...你的组合器是错误的。您不应该创建一个新的空结构并将作为参数提供的两个结构合并到这个新结构中。相反,您应该将第二个参数结构合并到第一个参数结构中。这就是为什么您的静态 linkTwoWalls 方法不起作用:您正在将两堵墙合并成一堵新墙,并且您正在从该静态方法返回这堵新墙。然而,返回的墙被丢弃了,因为组合器必须是一个 BiConsumer 将第二个参数合并到第一个参数中。 (你的实际上是一个 BinaryOperator,即你正在从两堵墙创建一堵新墙并将其返回,就像你将两个数字相加并得到另一个数字作为结果)。但是,您没有使用并行流,因此从未使用过您的组合器。