Странное поведение привязки переменных Java - PullRequest
0 голосов
/ 08 июня 2019

У меня есть следующий класс

public class Envirionment {

    private Square[][] grid;

    public Envirionment(int width, int height) {
        this.grid = new Square[height][width];
    }

    public void map(SquareActor actor) {
        for(int y = 0; y < grid.length; y++) {
            for(int x = 0; x < grid[y].length; x++) {
                actor.act(grid[y][x]);
            }
        }

    }
}

Чтобы проверить, что метод map работает правильно, я начал создавать тест, цель теста состояла в том, чтобы передать функцию, которая можетдействовать на квадраты и посмотреть, работает ли он правильно.

Одной из основных частей этого теста является проверка того, что он фактически проходит по всем квадратам, поэтому я подумал, что могу передать функцию, которая может связываться с переменными x и yцикл, вызов функции обратного вызова со значениями x и y и сохранение локального счетчика в тесте для сравнения и контраста.

Я начал с тестирования, чтобы выяснить, могу ли я передатьфункция, которая будет просто выводить переменные x и y, но я столкнусь с первым препятствием.

class EnvirionmentTest {

    @Test
    void test() {
        Envirionment env = new Envirionment(10, 10);
        env.map((square) -> System.out.println(String.format("%d, %d", x, y)));
        fail("Not yet implemented");
    }

}

Оказывается, компилятор начал кричать на мои, потому что x и y не могутразрешаться в переменную, однако я думал, что они будут привязаны к объему циклов, внутри которых они будут вызываться.

Можно ли установить привязку для этих переменных в Java?Или я подхожу к этой проблеме совершенно неправильно?


edit:

Я только что попробовал что-то похожее в python, и, похоже, оно работает нормально.

test = lambda : print(str(x) + "," str(y))
for y in range(2):
    for x in range(2):
        test()

вывод:

0,0
1,0
0,1
1,1

Ответы [ 2 ]

1 голос
/ 09 июня 2019

В вашем примере с Python вы создаете лямбда-выражение, которое будет иметь эквивалент в Java, например:

BiConsumer<String, String> biFunction = (x, y) -> System.out.println(x + "," + y);

В вашем примере вы получаете ошибку компиляции, потому что в этом выражении:

env.map((square) -> System.out.println(String.format("%d, %d", x, y)));

компилятор не знает, что такое x и y, и он прав.

Ваш метод Environment::map принимает аргумент типа SquareActor. Вы передаете лямбда-выражение в Environment::map, которое будет разрешено как экземпляр анонимного класса, реализующего SquareActor, который должен быть FunctionalInterface. В вашем случае ваше лямбда-выражение должно выглядеть как

env.map((square, x, y) -> System.out.println(String.format("%d, %d", x, y)));

но тогда он не будет соответствовать SquareActor::act сигнатуре метода. Java не поддерживает замыкания, которые вы думаете. Если вы хотите узнать, как реализовать замыкания в Java, проверьте в этой статье .

Ваш метод Environment::map просто перебирает внутренний массив и передает аргументы SquareActor::act. Таким образом, вы не должны иметь доступа к локальным счетчикам циклов внутри SquareActor::act не так ли? Если вы хотите протестировать свой метод, вы просто хотите проверить, будет ли ваша «фиктивная» реализация запускаться столько раз, сколько и должно быть:

env.map(square -> System.out.println(String.format("Square " + square)));

должно быть достаточно (если вы переопределите метод toString для класса Square.

1 голос
/ 09 июня 2019

В классе Square вы можете определить эти значения x и y как локальные переменные, а затем использовать лямбда-функцию следующим образом:

  env.map((square) -> System.out.println(String.format("%d, %d", square.x, square.y)));
...