简体   繁体   English

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

[英]Error Message in Java (Stream and Lambda Comprehension)

I've been stuck for several hours trying to debug my code. 我被困了几个小时试图调试我的代码。

this is the error message that I get 这是我收到的错误消息

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

these are the classes that corresponds to that error. 这些是与该错误对应的类。 So when I run the class DonalTrump it gave me the error message above. 因此,当我运行DonalTrumpDonalTrump它给了我上面的错误消息。 And apparently it is because of the Wall class. 显然是由于Wall类。 The following is my code 以下是我的代码

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

At first glance, I see 2 problems with your code: 乍一看,我发现您的代码有2个问题:

  1. You are not specifying a Supplier<Wall> in the call to collect in the DonaldTrump class, or you are doing it wrong. 您没有在要collectDonaldTrump类的调用中指定Supplier<Wall> ,或者做错了。 It's not correct to make your Wall class implement the Supplier<Wall> interface. 使Wall类实现Supplier<Wall>接口是不正确的。 Think of it in real life terms: it doesn't make sense that a wall is a supplier of itself. 用现实生活的角度来思考:一堵墙本身就是供应商,这毫无意义。 Instead, you should use a lambda expression that matches the Supplier interface, ie one that works as if you were implementing the Supplier.get method. 相反,您应该使用与Supplier接口匹配的lambda表达式,即,该表达式的工作方式就好像您正在实现 Supplier.get方法一样。 This is () -> new Wall(width, height) . 这是() -> new Wall(width, height)

  2. In your Wall class, both the accept and combine methods shouldn't be static. Wall类中, acceptcombine方法都不应该是静态的。 Besides, accept should not revceive an instance of Wall , but only accept a Brick , which will be put into this Wall . 此外, accept不应revceive的实例Wall ,但只接受一个Brick ,将被放入这个Wall Also, the combine method should accept only one Wall argument and combine this argument with this Wall . 同样, combine方法仅应接受一个Wall参数并将此参数与此Wall Maybe you could read the lesson about method references in The Java Tutorial , which clearly explains all the different method reference types and when to use each one of them. 也许您可以阅读The Java Tutorial中有关方法引用的课程 ,该课程清楚地解释了所有不同的方法引用类型以及何时使用它们中的每一种。

Taking these items into account means that you should perform a few changes to your code. 考虑这些因素意味着您应该对代码进行一些更改。

In your DonaldTrump class, place the () -> new Wall(width, height) lambda expression as the Supplier of the collect method: 在您的DonaldTrump类中,将() -> 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);

And in your Wall class, change the accept and combine method as follows: 在您的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);
}

Where linkToThisWall would be a modified version of your (now useless) linkTwoWalls method: 其中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;
}

Consider also removing the get method, as implementing Supplier<Wall> is no longer needed. 考虑也删除get方法,因为不再需要实现Supplier<Wall>

Actually, with this code fix and refactoring, you no longer need the accept and combine methods. 实际上,通过此代码修复和重构,您不再需要acceptcombine方法。 In your DonaldTrump class, you could just use references to the refactored lay and linkToThisWall methods instead: 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);

EDIT: The main reason for these changes is that you were not using the Stream.collect method correctly. 编辑:这些更改的主要原因是您没有正确使用Stream.collect方法。

Stream.collect expects 3 arguments: Stream.collect需要3个参数:

  1. A supplier that will be used to create a cumulative, mutable structure where to accumulate the elements of the stream. 供应商,该供应商将用于创建累积的可变结构,以在其中累积流的元素。 In your code, this structure is a Wall and the elements of the stream are instances of Brick , so the supplier is () -> new Wall(width, height) . 在您的代码中,此结构是Wall ,流的元素是Brick实例,因此供应商是() -> new Wall(width, height) This supplier might be seen as an empty wall, ie like a place in the ground where to start laying bricks. 该供应商可能被视为空墙,即像是地面上开始砌砖的地方。
  2. An accumulator, which is a BiConsumer that accepts two arguments: the structure returned by the previous item's supplier and an element of the stream. 一个累加器,它是一个BiConsumer ,它接受两个参数:上一项的供应商返回的结构和流的元素。 The contract for this accumulator biconsumer is that it has to accumulate one element of the stream into the cumulative, mutable structure. 此累加器双消耗者的契约是它必须将流的一个元素累加到累加的可变结构中。 In your case, the cumulative, mutable structure is the Wall created by the supplier above and the elements of the stream are instances of Brick , so the accumulator is Wall::lay , or using a lambda (wall, brick) -> wall.lay(brick) . 在您的情况下,累积的可变结构是上面的供应商创建的Wall ,而流的元素是Brick实例,因此累加器是Wall::lay或使用lambda (wall, brick) -> wall.lay(brick) This accumulator might be seen as a worker laying bricks into a wall, one by one. 可以将这种蓄能器看作是一名工人一个接一个地将砖砌成的墙壁。
  3. A combiner, which is a BiConsumer that accepts two arguments, both being instances of a partially filled, mutable structure (these structures have the same type as the structure supplied by the Supplier of item 1). 组合器,它是一个BiConsumer ,它接受两个参数,两个参数都是部分填充的可变结构的实例(这些结构的类型与项目1的Supplier提供的结构相同)。 This combiner is to be used when the creation of the final structure can be parallelized, and its contract is to combine (or merge, or mix, or link or join) the second argument structure into the first argument structure. 当最终结构的创建可以并行化时,将使用此合并器,并且其合同是将第二个自变量结构合并(或合并,混合,链接或联接)到第一个自变量结构中。 In your case, the partially filled, mutable structures are two Wall instances partially filled with bricks, so the combiner is Wall::linkToThisWall , or using a lambda (leftWall, rightWall) -> leftWall.linkToThisWall(rightWall) . 在您的情况下,部分填充的可变结构是两个用砖块部分填充的Wall实例,因此组合器是Wall::linkToThisWall ,或者使用lambda (leftWall, rightWall) -> leftWall.linkToThisWall(rightWall) All this combining stuff might be seen as two separate workers working in parallel, each one working by laying bricks into his own wall: one worker would start on the left and the other one would start in the right; 所有这些结合在一起的东西可能被看作是两个独立的工人并行工作,每个工人都在自己的墙壁上砌砖工作:一个工人从左边开始,另一个工人从右边开始; when they meet in the middle, both half walls are combined into a new full wall. 当它们在中间相遇时,两个半墙合并为一个新的全墙。

As to why your solution was incorrect... Your combiner was wrong. 至于为什么您的解决方案不正确...您的组合器是错误的。 You are not supposed to create a new, empty structure and merge the two structures provided as arguments into this new structure. 您不应该创建一个新的空结构,并将作为参数提供的两个结构合并到这个新结构中。 Instead, you should merge the second argument structure into the first argument structure. 相反,您应该将第二个参数结构合并到第一个参数结构中。 This is why your static linkTwoWalls method didn't work: you were merging two walls into a new one and you were returning this new wall from that static method. 这就是为什么您的静态linkTwoWalls方法不起作用的原因:您将两堵墙合并为新的墙,并且从该静态方法返回了这堵新墙。 However, that returned wall was being discarded, because the combiner must be a BiConsumer that merges second into first argument. 但是,返回的墙已被丢弃,因为合并器必须是将第二个合并到第一个参数中的BiConsumer (Yours was actually a BinaryOperator , ie you were creating a new wall from two walls and returning it, as when you sum two numbers and get another number as a result). (您实际上是BinaryOperator ,即您是从两个墙中创建一个新墙并将其返回,就像您将两个数字求和并得到另一个数字一样)。 However, you were not using a parallel stream, so your combiner was never used. 但是,您没有使用并行流,因此从未使用过合并器。

You'll need to override the get method of the Supplier interface. 您将需要覆盖Supplier接口的get方法。

public Wall get(){
      return this;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM