Reputation: 85
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. And apparently it is because of the Wall
class. 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);
}
}
Upvotes: 1
Views: 700
Reputation: 34460
At first glance, I see 2 problems with your code:
You are not specifying a Supplier<Wall>
in the call to collect
in the DonaldTrump
class, or you are doing it wrong. It's not correct to make your Wall
class implement the Supplier<Wall>
interface. 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, i.e. one that works as if you were implementing the Supplier.get
method. This is () -> new Wall(width, height)
.
In your Wall
class, both the accept
and combine
methods shouldn't be static. Besides, accept
should not revceive an instance of Wall
, but only accept a Brick
, which will be put into this Wall
. Also, the combine
method should accept only one Wall
argument and combine this argument with this 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.
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:
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:
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:
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.
Actually, with this code fix and refactoring, you no longer need the accept
and combine
methods. In your DonaldTrump
class, you could just use references to the refactored lay
and linkToThisWall
methods instead:
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
expects 3 arguments:
Wall
and the elements of the stream are instances of Brick
, so the supplier is () -> new Wall(width, height)
. This supplier might be seen as an empty wall, i.e. like a place in the ground where to start laying bricks.BiConsumer
that accepts two arguments: the structure returned by the previous item's supplier and an element of the stream. 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)
. This accumulator might be seen as a worker laying bricks into a wall, one by one.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). 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)
. 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. However, that returned wall was being discarded, because the combiner must be a BiConsumer
that merges second into first argument. (Yours was actually a BinaryOperator
, i.e. 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). However, you were not using a parallel stream, so your combiner was never used.
Upvotes: 2
Reputation: 56423
You'll need to override the get
method of the Supplier interface.
public Wall get(){
return this;
}
Upvotes: 0