代码之家  ›  专栏  ›  技术社区  ›  banan3'14

Lambda作为Java中带有参数和原语的方法

  •  4
  • banan3'14  · 技术社区  · 7 年前

    我有一个在国际象棋板上做操作的班级。每个细胞都有它的状态。电路板是一个数组,其声明为:

    private CellState[][] cellBoard = new CellState[8][8];
    

    private void traverseBoard(Command method) {
        for(int row = 0; row < 8; row++) {
            for(int file = 0; file < 8; file++) {
                method.execute(row, file, cellBoard[row][file]);
            }
        }
    }
    
    protected interface Command {
        public void execute(int x, int y, CELL_STATE state);
    }
    

    在其中一种方法中,我检查每个单元格是否为空:

    private boolean areAllEmpty() {
        ExtendedBoolean empty = new ExtendedBoolean(true);
        traverseBoard((i, j, state) -> {
            if (state != CELL_STATE.EMPTY) {
                empty.set(false);
            }
        });
        return empty.is();
    }
    

    我不能使用原始 boolean ,因为它是不可变的。为此,我创建了一个嵌套类:

    private class ExtendedBoolean {
        boolean bool;
    
        public ExtendedBoolean(boolean bool) {
            this.bool = bool;
        }
    
        public void set(boolean bool) {
            this.bool = bool;
        }
    
        public boolean is() {
            return bool;
        }
    }
    

    我希望有更好的方法在lambda表达式中传递具有多个参数的方法。我知道可以使用 Runnable like in this answer

    我希望我不必写信 ExtendedBoolean ExtendedInteger

    我的希望合理吗?

    编辑: 细胞状态的集合必须是可变的。我改变细胞的状态,然后检查它们。我在一个循环中进行,直到条件满足为止。

    5 回复  |  直到 7 年前
        1
  •  3
  •   JB Nizet    7 年前

    您可以使用流来表示电路板,从而受益于所有流方法:

        Stream<Cell> stream =
            IntStream.range(0, 64)
                .mapToObj(i -> {
                    int row = i / 8;
                    int column = i % 8;
                    return new Cell(row, column, cellBoard[row][column]);
                });
    

    其中,单元类将定义为

    private static class Cell {
        private final int row;
        private final int column;
        private final CELL_STATE state;
    
        public Cell(int row, int column, CELL_STATE state) {
            this.row = row;
            this.column = column;
            this.state = state;
        }
    
        public int getRow() {
            return row;
        }
    
        public int getColumn() {
            return column;
        }
    
        public CELL_STATE getState() {
            return state;
        }
    
        @Override
        public String toString() {
            return "Cell{" +
                "row=" + row +
                ", column=" + column +
                ", state=" + state +
                '}';
        }
    }
    

    stream.allMatch(cell -> cell.getState() == CELL_STATE.EMPTY);
    
        2
  •  2
  •   tsolakp    7 年前

    您可以尝试将泛型和lambda结合在一起,如下所示:

    protected interface Command<T> {
        public T execute(T prev, int x, int y, CELL_STATE state);
    }
    
    
    private <T> T traverseBoard(T initial, Command<T> method) {
        T result = initial;
        for(int row = 0; row < 8; row++) {
            for(int file = 0; file < 8; file++) {
                result = method.execute( result, row, file, null );
            }
        }
        return result;
    }
    
    private boolean areAllEmpty() {
        return traverseBoard( Boolean.FALSE, (b, i, j, state) -> {
            if ( state != CELL_STATE.EMPTY ) {
                return false;
            }else{
                return true;
            }
        } );
    }
    
    private int count() {
        return traverseBoard( 0, (c, i, j, state) -> c += 1 );
    }
    

    我还举了一个例子,说明如何进行计数,以查看状态是如何维护的。

    但我喜欢用流来回答问题。

        3
  •  1
  •   OldCurmudgeon    7 年前

    public class Mutable<T> {
        T it;
    
        public Mutable(T it) {
            this.it = it;
        }
    
        public void set(T it) {
            this.it = it;
        }
    
        public T get() {
            return it;
        }
    }
    

    然后可以使用 Mutable<Boolean> boolean .

        4
  •  0
  •   Edu G    7 年前

    在lambdas中,从外部作用域(clojure)捕获的所有变量都需要是最终的,或者实际上是最终的。这就是为什么它不允许您直接更改变量的原因,如果clojure的Javas实现与C#或JavaScript的实现类似,那么您就不需要使用它。

    boolean[] empty = new boolean[]{false};
    traverseBoard((i,j,state) -> empty[0] = true ) // Simple usage
    

    我认为这个解决方案(我的观点)很难看,另一个解决方案是创建一个Holder类,这是泛型的,所以当你需要类似的东西时,你可以使用它。

    例如:

    Holder<Student> studentHolder = new Holder<>(student); doSomething( anotherStudent -> studentHolder.value = anotherStudent );

    BooleanHolder boolHolder = new BooleanHolder(false);
    
        5
  •  0
  •   fps    7 年前

    你的 traverseBoard 如果您只想对每个单元格执行一些操作,则该方法是正确的。然而,如果需要检查每个单元格的某些条件,则需要另一种方法。

    在我看来,问题是你需要设计一个方法来遍历你的电路板并返回一个 boolean void .首先要做的是:

    private boolean allCellsInBoardMatch(Condition condition) {
        for (int row = 0; row < 8; row++) {
            for (int file = 0; file < 8; file++) {
                if (!condition.check(row, file, cellBoard[row][file])) {
                    return false;
                }
            }
        }
        return true;
    }
    
    protected interface Condition {
        boolean check(int x, int y, CELL_STATE state);
    }
    

    private boolean areAllEmpty() {
        return allCellsInBoardMatch((i, j, state) -> state == CELL_STATE.EMPTY);
    }