【问题标题】:Error Message in Java (Stream and Lambda Comprehension)Java 中的错误消息(流和 Lambda 理解)
【发布时间】:2017-05-11 14:48:29
【问题描述】:

我被困了好几个小时试图调试我的代码。

这是我收到的错误消息

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

这些是对应于该错误的类。因此,当我运行 DonalTrump 课程时,它给了我上面的错误消息。显然这是因为Wall 类。以下是我的代码

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 "\033[31m" + buffer.toString() + "\033[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);
    }
}

【问题讨论】:

  • 你说Wall实现Supplier...那你没有实现。
  • #Rekt。但说真的,这是一个真正的问题吗?
  • 是的@Carcigenicate 有什么想法吗?
  • @Carcigenicate 似乎是这样。编译错误消息再清楚不过了。
  • 我认为您应该使用() -&gt; new Wall(width, height) 作为您的供应商。供应商提供将累积结果的项目。

标签: java lambda java-8 java-stream collectors


【解决方案1】:

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

  1. 您没有在DonaldTrump 类中对collect 的调用中指定Supplier&lt;Wall&gt;,或者您做错了。让你的Wall 类实现Supplier&lt;Wall&gt; 接口是不正确的。用现实生活的术语来想一想:一堵墙是它自己的供应商是没有意义的。相反,您应该使用与 Supplier 接口匹配的 lambda 表达式,即就像您正在实现 Supplier.get 方法一样工作的表达式。这是() -&gt; new Wall(width, height)

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

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

在您的DonaldTrump 类中,将() -&gt; 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 类中,更改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&lt;Wall&gt;

实际上,通过此代码修复和重构,您不再需要 acceptcombine 方法。在您的 DonaldTrump 类中,您可以只使用对重构的 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 的实例,因此供应商是() -&gt; new Wall(width, height)。该供应商可能会被视为一堵空墙,即像地面上开始砌砖的地方。
  2. 一个累加器,它是一个BiConsumer,它接受两个参数:前一个项目的供应商返回的结构和流的一个元素。这个累加器双消费者的约定是它必须将流的一个元素累加到可累加的可变结构中。在您的情况下,累积的可变结构是上面的供应商创建的Wall,流的元素是Brick 的实例,所以累加器是Wall::lay,或者使用lambda (wall, brick) -&gt; wall.lay(brick)。这个蓄能器可以被看作是一个把砖块一块一块地砌到墙上的工人。
  3. 一个组合器,它是一个BiConsumer,它接受两个参数,这两个参数都是部分填充的可变结构的实例(这些结构与第1 项的Supplier 提供的结构具有相同的类型)。当最终结构的创建可以并行化时,将使用此组合器,其约定是将第二个参数结构组合(或合并、或混合、或链接或连接)到第一个参数结构中。在您的情况下,部分填充的可变结构是两个部分填充砖块的Wall 实例,因此组合器是Wall::linkToThisWall,或使用lambda (leftWall, rightWall) -&gt; leftWall.linkToThisWall(rightWall)。所有这些结合在一起的东西可以看作是两个独立的工人并行工作,每个工人都在自己的墙上砌砖:一个工人从左边开始,另一个从右边开始;当它们在中间相遇时,两个半墙组合成一个新的完整墙。

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

【讨论】:

  • 我已根据您的建议更新了代码。你能再检查一遍吗?此外,当我尝试运行它时,这就是我得到的Error:(44, 17) java: no suitable method found for collect(()-&gt;new Wa[...]ight),Wall::accept,Wall::combine) method java.util.stream.Stream....
  • 当我介绍 get() 方法时,代码实际上是有效的。但是,当 accept() 和 combine method() 都是静态的时,这有效。但是当我消除静电时。它产生一个错误(上面的评论)
  • 非常感谢,这实际上是我的导师想要的方法。你能向我解释一下为什么这比我之前使用 get() 方法得到的工作解决方案更好,并且都接受和组合为静态的。
【解决方案2】:

您需要覆盖供应商接口的get 方法。

public Wall get(){
      return this;
}

【讨论】:

  • 但是Supplier 接口是内置的。它是 Java 8 的一部分。如何更改它?
  • 对不起,我没听懂你..?
  • 是的,它是内置的。但它是一个接口。因此,当您声明一个类实现了该接口(这是Wall 所做的)时,您需要声明get() 方法(因为它必须存在于Supplier 的所有实例中)。您没有在 Wall 中指定 get() 方法。
  • @Kutam 你说你的类实现了供应商。您至少需要上面的代码来实现供应商。这与更改 Supplier 接口没有任何关系,只是实现 Supplier 接口的所需方法。
  • @Kutam 请不要更改问题的上下文(不要在您的问题已经解决时编辑问题),问题你现在得到的是另一回事。这与您的实施有关,而不是我建议的。
猜你喜欢
  • 2014-05-04
  • 2019-04-19
  • 2019-06-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多