Подтвердить что ты не робот

Анонимный класс заменен на лямбда-выражения

У меня есть пример кода, который использует Java 8 новую функцию потока (получите диапазон значений int 1.. 20, пропустите первые 9, затем возьмите оставшиеся 10, каждая величина int: уменьшает на единицу и умножается на 2).

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand * 2;
        }
    })).toArray()));

Выход выглядит следующим образом:

[18, 20, 22, 24, 26, 28, 30, 32, 34, 36]

Теперь я хочу заменить анонимные классы лямбда-выражениями. Следующее преобразование работает отлично (второй анонимный класс заменен выражением i -> i * 2 лямбда), и я получаю тот же вывод:

System.out.println(Arrays.toString(
    IntStream.rangeClosed(1, 20).skip(9).limit(10).map((new IntUnaryOperator() {
        @Override
        public int applyAsInt(int operand) {
            return operand - 1;
        }
    }).andThen(i -> i * 2)).toArray()));

Однако, когда я заменяю первый анонимный класс выражением лямбда:

System.out.println(
    Arrays.toString(
        IntStream.rangeClosed(1, 20).skip(9).limit(10)
            .map((v -> v - 1).andThen(i -> i * 2)).toArray()));

Я не могу скомпилировать свой код. Ошибка говорит Operator '-' cannot be applied to '<lambda parameter>', 'int'

Compilation error

Знаете ли вы, как объединить два лямбда-выражения с IntUnaryOperator.andThen?

Я знаю, что могу использовать... .limit(10).map(v -> v - 1).map(i -> i * 2).toArray() ..., который отлично работает, но я хотел бы узнать, как использовать IntUnaryOperator.andThen с lambdas (если это возможно).

4b9b3361

Ответ 1

Вы должны явно перенести первое лямбда-выражение в IntUnaryOperator. Работает следующий код:

System.out.println(
        Arrays.toString(
                IntStream.rangeClosed(1, 20).skip(9).limit(10)
                        .map(((IntUnaryOperator) v -> v - 1).andThen(i -> i * 2)).toArray()));

Ответ 2

Мне удалось скомпилировать код следующим образом (хотя использование экспериментальных версий eclipse):

IntUnaryOperator first = v -> v - 1;
IntUnaryOperator second = i -> i * 2;
System.out.println(Arrays.toString(IntStream.rangeClosed(1, 20).skip(9)
                .limit(10).map(first.andThen(second)).toArray()));

Я понимаю ваш интерес к написанию кода без объявления first и second; однако IntUnaryOperator является FunctionalInterface и на основе того, как работает Java-компилятор, FunctionalInterface должен иметь "контекст", чтобы быть в состоянии быть таким, каким вы его хотите. Вот почему с вашим исходным фрагментом кода Java-компилятор не имеет возможности отображать ваше выражение lamda точно в экземпляр IntUnaryOperator.

Для более продолжительного обсуждения взгляните на эту тему: http://mail.openjdk.java.net/pipermail/jdk8-dev/2013-June/002668.html

Ответ 3

Другим решением является использование статического IntUnaryOperator.identity() в качестве начальной точки:

IntUnaryOperator.identity().andThen(v -> v - 1).andThen(i -> i * 2)

Со статическим импортом вы можете использовать только identity() тоже!

Если IntUnaryOperator имел статический метод, например...

static IntUnaryOperator first(IntUnaryOperator op) { return op; }

... мы могли бы просто написать:

IntUnaryOperator.first(v -> v - 1).andThen(i -> i * 2)