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

Lambdas в классическом примере перечисления

Как многие из вас могут знать, есть классический пример Operation enum (теперь с использованием стандартного интерфейса Java 8), то есть следующее:

enum Operation implements DoubleBinaryOperator {
    PLUS("+") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left + right;
        }
    },
    MINUS("-") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left - right;
        }
    },
    MULTIPLY("*") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left * right;
        }
    },
    DIVIDE("/") {
        @Override
        public double applyAsDouble(final double left, final double right) {
            return left / right;
        }
    };

    private final String symbol;

    private Operation(final String symbol) {
        this.symbol = symbol;
    }

    public String getSymbol() {
        return symbol;
    }
}

Протестировано с помощью

Arrays.stream(Operation.values())
        .forEach(op -> System.out.println("Performing operation " + op.getSymbol() + " on 2 and 4: " + op.applyAsDouble(2, 4)));

Он обеспечивает:

Выполнение операции + на 2 и 4: 6,0
Выполнение операции - на 2 и 4: -2,0
Выполнение операции * по 2 и 4: 8,0
Выполнение операции/по 2 и 4: 0,5

Но я чувствую, что мы можем сделать лучше с Java 8, поэтому я реализовал следующее:

enum Operation implements DoubleBinaryOperator {
    PLUS    ("+", (l, r) -> l + r),
    MINUS   ("-", (l, r) -> l - r),
    MULTIPLY("*", (l, r) -> l * r),
    DIVIDE  ("/", (l, r) -> l / r);

    private final String symbol;
    private final DoubleBinaryOperator binaryOperator;

    private Operation(final String symbol, final DoubleBinaryOperator binaryOperator) {
        this.symbol = symbol;
        this.binaryOperator = binaryOperator;
    }

    public String getSymbol() {
        return symbol;
    }

    @Override
    public double applyAsDouble(final double left, final double right) {
        return binaryOperator.applyAsDouble(left, right);
    }
}

Функционально это эквивалентно, однако обе реализации все еще похожи, или есть некоторые скрытые детали, которые делают новую версию хуже старой версии?

И, наконец, лямбда-путь предпочтительный способ сделать это с Java 8?

4b9b3361

Ответ 1

Очевидно, что лямбда-версия более читаема. Он не только короче, но и позволяет читателю увидеть оператор реализации с первого взгляда в конструкторе. Представьте, что вы хотите расширить enum для поддержки расчета int...

С точки зрения производительности вы обмениваете анонимные внутренние классы enum сгенерированными лямбда-классами. Версия лямбда добавляет еще один уровень делегирования, но это не проблема для оптимизатора HotSpot. Его вряд ли заметят какие-либо различия в производительности исполнения.

Однако при применении шаблона лямбда, следовательно, вы можете ускорить запуск приложений с использованием класса. Причина в том, что для традиционного специализированного подхода enum компилятор Java должен генерировать внутренний класс для каждого случая, который находится либо в файловой системе, либо (возможно, с zip-сжатым) в файле Jar. Генерирование байтового кода для класса лямбда (который имеет очень простую структуру) "на лету" обычно быстрее, чем загрузка класса. Это также может помочь, если нет проверки доступа к сгенерированным лямбда-классам.

Подводя итог этому:

  • Лямбда-подход легче читать, а его код более удобен в обслуживании (большая точка).
  • Производительность выполнения примерно такая же
  • Время запуска может быть меньше для лямбда-подхода

Итак, это большая победа для лямбды. Да, я думаю, что лямбда-путь предпочтительный способ сделать это с Java 8.

Ответ 2

Это зависит от того, как вы лучше определяете.

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

enum Operation implements DoubleBinaryOperator {
    PLUS    ("+", Double::sum),
    ...
}

Это короткий и читаемый. Я не думаю, что что-то разумное можно сказать о производительности без сравнения вашего кода.

Lambdas реализованы с помощью invokeDynamic для динамического связывания сайта вызова с фактическим кодом для выполнения; нет анонимных, внутренних классов.

Ответ 3

Определите хуже, скорее всего, он использует немного более байт-код и немного медленнее.

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

Ответ 4

Я удивлен, что никто не упомянул об этом, но подход лямбды может стать привлекательным при применении нескольких методов. Передача кучи безымянных лямбда в конструктор будет более кратким, но не обязательно более читаемым.

Кроме того, преимущество использования лямбда уменьшается по мере увеличения размера функции. Если ваша лямбда длиннее нескольких строк, переопределение может быть так же легко читать, если не проще.