Лямбда как аргумент метода [duplicate]

Давайте посмотрим на лес сначала, прежде чем смотреть на деревья.

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

  1. Ваша точка входа (ов) выполняется в результате события. Например, в браузер загружается тег сценария с кодом. (Соответственно, поэтому вам, возможно, придется заботиться о готовности страницы запускать ваш код, если он требует, чтобы элементы dom были сконструированы первыми и т. Д.)
  2. Ваш код выполняется до завершения, однако многие асинхронные вызовы, которые он делает, без выполнения каких-либо ваших обратных вызовов, включая запросы XHR, установку тайм-аутов, обработчиков событий dom и т. д. Каждый из этих обратных вызовов, ожидающих выполнения, будет находиться в очереди, ожидая, что их очередь будет запущена после других событий
  3. Каждый отдельный обратный вызов XHR-запроса, установленного таймаута или dom события после вызова будет завершен.

Хорошие новости заключается в том, что, если вы хорошо понимаете этот момент, вам никогда не придется беспокоиться о гоночных условиях. Прежде всего вы должны понимать, как вы хотите упорядочить свой код как по существу ответ на разные дискретные события, и как вы хотите объединить их в логическую последовательность. Вы можете использовать обещания или новые асинхронные / ожидающие более высокие уровни в качестве инструментов для этой цели, или вы можете откатывать свои собственные.

Но вы не должны использовать какие-либо тактические инструменты для решения проблемы, пока вам не понравится актуальная проблемная область. Нарисуйте карту этих зависимостей, чтобы знать, что нужно запускать, когда. Попытка ad-hoc подхода ко всем этим обратным вызовам просто не поможет вам.

12
задан Sarah Phillips 17 July 2015 в 18:08
поделиться

3 ответа

Это было одно из самых сложных и широко обсуждаемых решений в Группе экспертов JSR-335. С одной стороны, представляется вполне разумным, что абстрактный абстрактный метод может быть разумной целью преобразования для лямбда. И, если ваша ментальная модель «лямбда-это просто компактные анонимные классы», тогда это было бы вполне разумной идеей.

Однако, если вы натягиваете эту строку на некоторое время, вы понимаете, что она тащит с у вас много сложностей и ограничений - ради использования меньшинства.

Одна из худших вещей, которые тащит с собой, - это значение имен внутри тела лямбды, а в качестве особого случая - значение this. Внутри внутреннего класса существует ужасно сложное правило поиска («поиск гребня»), поскольку имена внутри внутреннего класса могут относиться к членам супертипа или быть захваченным из лексической среды. (Например, многие ошибки и головоломки вращаются вокруг, используя this, а не Outer.this, во внутренних телах класса.) Если бы мы разрешили преобразование лямбда в абстрактные классы SAM, у нас было бы два плохих выбора; загрязнять все лямбды со сложной сложностью определения внутреннего класса или разрешать преобразование в объекты абстрактного класса, но ограничивать доступ таким образом, чтобы тело лямбда не могло ссылаться на членов базового класса (что вызовет его собственную путаницу). получившееся правило получается очень чистым: кроме форматов лямбда-параметров имена (в том числе this, которые являются только именем) внутри тела лямбды означают то, что они означают сразу за пределами лямбда-тела.

Другая проблема, связанная с переходом lambdas во внутренние классы, связана с идентификацией объекта и сопутствующей потерей оптимизации VM. У выражения внутреннего класса (например, new Foo() { }) гарантируется уникальный идентификатор объекта. Не совершая так сильно идентификацию объекта для lambdas, мы освобождаем виртуальную машину, чтобы сделать много полезных оптимизаций, которые она в противном случае не могла бы сделать. (В результате лямбда-связь и захват уже быстрее, чем для анонимных классов, и до сих пор существует глубокий трубопровод оптимизации, который мы еще не применили.)

Кроме того, если у вас есть одно- метод абстрактного класса и хотите иметь возможность использовать lambdas для их создания, есть простой путь для этого - определить фабричный метод, который использует функциональный интерфейс в качестве аргумента. (Мы добавили фабричный метод для ThreadLocal в Java 8, который делает это.)

Последний гвоздь в гробу для «lambdas - просто удобный синтаксис для объектов», взгляд мира появился после того, как мы сделали анализ существующих кодовых баз и их использование интерфейсов с одним абстрактным методом и абстрактных классов. Мы обнаружили, что только очень небольшой процент был основан на абстрактных классах. Было глупо обременять все лямбды сложностью и проблемами производительности подхода, который только выиграл бы менее 1% использования. Поэтому мы приняли «смелое» решение отказаться от этого варианта использования, чтобы воспользоваться преимуществами, которые это обеспечило для других 99 +%.

18
ответ дан Brian Goetz 26 August 2018 в 10:07
поделиться

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

Например. выражение лямбда не наследует членов от типа, который он в конечном итоге реализует. Таким образом, в вашем случае это не сработает, даже если RouteBuilder был функциональным интерфейсом, поскольку ваше выражение лямбда не наследует метод from, который вы хотите вызвать. Точно так же значения this и super такие же, как и вне лямбда-выражения, и не относятся к экземпляру, который будет представлять функцию после этого (т. Е. Экземпляр RouteBuilder).

сказал, было бы нецелесообразно расширять функцию для реализации классов abstract, которые ведут себя как interface s, но это накладывает несколько ограничений, которые трудно проверить. Хотя легко убедиться, что класс имеет ровно один метод abstract и доступный конструктор no-arg, класс также должен быть свободен от любого изменяемого состояния, а построение экземпляра этого класса также должно быть свободным от побочных эффектов , так что свобода JVM кэшировать и повторно использовать экземпляры lambda и делиться ими между разными сайтами создания не влияет на поведение программы.

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

6
ответ дан Holger 26 August 2018 в 10:07
поделиться

В дополнение к другим замечательным ответам следует упомянуть, что если вам действительно нужно создавать такие RouteBuilder объекты очень часто, вы можете создать вспомогательный метод, подобный этому:

public static RouteBuilder fromConfigurator(Consumer<RouteBuilder> configurator) {
    return new RouteBuilder() {
        public void configure() {
            configurator.accept(this);
        }
    }
}

И использовать его например:

context.addRoutes(fromConfigurator(
    rb->rb.from("file:data/inbox?noop=true").to("file:data/outbox")));
5
ответ дан Tagir Valeev 26 August 2018 в 10:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: