Давайте посмотрим на лес сначала, прежде чем смотреть на деревья.
Здесь есть много информативных ответов с большими подробностями, я не буду повторять ни одного из них. Ключ к программированию в JavaScript имеет сначала правильную ментальную модель общего исполнения.
Хорошие новости заключается в том, что, если вы хорошо понимаете этот момент, вам никогда не придется беспокоиться о гоночных условиях. Прежде всего вы должны понимать, как вы хотите упорядочить свой код как по существу ответ на разные дискретные события, и как вы хотите объединить их в логическую последовательность. Вы можете использовать обещания или новые асинхронные / ожидающие более высокие уровни в качестве инструментов для этой цели, или вы можете откатывать свои собственные.
Но вы не должны использовать какие-либо тактические инструменты для решения проблемы, пока вам не понравится актуальная проблемная область. Нарисуйте карту этих зависимостей, чтобы знать, что нужно запускать, когда. Попытка ad-hoc подхода ко всем этим обратным вызовам просто не поможет вам.
Это было одно из самых сложных и широко обсуждаемых решений в Группе экспертов JSR-335. С одной стороны, представляется вполне разумным, что абстрактный абстрактный метод может быть разумной целью преобразования для лямбда. И, если ваша ментальная модель «лямбда-это просто компактные анонимные классы», тогда это было бы вполне разумной идеей.
Однако, если вы натягиваете эту строку на некоторое время, вы понимаете, что она тащит с у вас много сложностей и ограничений - ради использования меньшинства.
Одна из худших вещей, которые тащит с собой, - это значение имен внутри тела лямбды, а в качестве особого случая - значение this
. Внутри внутреннего класса существует ужасно сложное правило поиска («поиск гребня»), поскольку имена внутри внутреннего класса могут относиться к членам супертипа или быть захваченным из лексической среды. (Например, многие ошибки и головоломки вращаются вокруг, используя this
, а не Outer.this
, во внутренних телах класса.) Если бы мы разрешили преобразование лямбда в абстрактные классы SAM, у нас было бы два плохих выбора; загрязнять все лямбды со сложной сложностью определения внутреннего класса или разрешать преобразование в объекты абстрактного класса, но ограничивать доступ таким образом, чтобы тело лямбда не могло ссылаться на членов базового класса (что вызовет его собственную путаницу). получившееся правило получается очень чистым: кроме форматов лямбда-параметров имена (в том числе this
, которые являются только именем) внутри тела лямбды означают то, что они означают сразу за пределами лямбда-тела.
Другая проблема, связанная с переходом lambdas во внутренние классы, связана с идентификацией объекта и сопутствующей потерей оптимизации VM. У выражения внутреннего класса (например, new Foo() { }
) гарантируется уникальный идентификатор объекта. Не совершая так сильно идентификацию объекта для lambdas, мы освобождаем виртуальную машину, чтобы сделать много полезных оптимизаций, которые она в противном случае не могла бы сделать. (В результате лямбда-связь и захват уже быстрее, чем для анонимных классов, и до сих пор существует глубокий трубопровод оптимизации, который мы еще не применили.)
Кроме того, если у вас есть одно- метод абстрактного класса и хотите иметь возможность использовать lambdas для их создания, есть простой путь для этого - определить фабричный метод, который использует функциональный интерфейс в качестве аргумента. (Мы добавили фабричный метод для ThreadLocal
в Java 8, который делает это.)
Последний гвоздь в гробу для «lambdas - просто удобный синтаксис для объектов», взгляд мира появился после того, как мы сделали анализ существующих кодовых баз и их использование интерфейсов с одним абстрактным методом и абстрактных классов. Мы обнаружили, что только очень небольшой процент был основан на абстрактных классах. Было глупо обременять все лямбды сложностью и проблемами производительности подхода, который только выиграл бы менее 1% использования. Поэтому мы приняли «смелое» решение отказаться от этого варианта использования, чтобы воспользоваться преимуществами, которые это обеспечило для других 99 +%.
Лямбда-выражения определяют функции не методами. Очевидно, что между ними существует техническая взаимосвязь, но в концептуальном представлении она отличается и как она работает на уровне исходного кода.
Например. выражение лямбда не наследует членов от типа, который он в конечном итоге реализует. Таким образом, в вашем случае это не сработает, даже если RouteBuilder
был функциональным интерфейсом, поскольку ваше выражение лямбда не наследует метод from
, который вы хотите вызвать. Точно так же значения this
и super
такие же, как и вне лямбда-выражения, и не относятся к экземпляру, который будет представлять функцию после этого (т. Е. Экземпляр RouteBuilder
).
сказал, было бы нецелесообразно расширять функцию для реализации классов abstract
, которые ведут себя как interface
s, но это накладывает несколько ограничений, которые трудно проверить. Хотя легко убедиться, что класс имеет ровно один метод abstract
и доступный конструктор no-arg, класс также должен быть свободен от любого изменяемого состояния, а построение экземпляра этого класса также должно быть свободным от побочных эффектов , так что свобода JVM кэшировать и повторно использовать экземпляры lambda и делиться ими между разными сайтами создания не влияет на поведение программы.
Это трудно проверить и хорошо, в большинстве случаев , ограничения не выполняются, поскольку в этом причина использования abstract class
, а не interface
в первую очередь. Он мог бы работать, если бы лямбда-выражения были просто заменой для внутренних классов, и поэтому совместное использование и повторное использование экземпляров не было разрешено, но это не то, что лямбда-выражения, даже если они используются как простая замена внутреннего класса в во многих случаях, не задумываясь о функциональном программировании ...
В дополнение к другим замечательным ответам следует упомянуть, что если вам действительно нужно создавать такие 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")));