Это слишком дорого, чтобы сделать (Runnable & Serializable), когда вы хотите отправить анонимную функцию?


Я делаю sht, как:

 executor.execute((Runnable & Serializable)func);

Где func является анонимной функцией, я должен активно использовать это в проекте, иначе мне пришлось бы создавать класс для каждой другой функции, которую я хочу вызвать, и реализовывать Runnable и Serializable в каждом из этих классов, преимуществом было бы то, что у меня был бы тип во время компиляции, а не приведение его во время выполнения, я хотел бы знать, является ли выполнение этого приведения слишком дорогостоящим или тривиальным и не представляет большого разрыва в производительности.

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

Заранее благодарю.

3 3

3 ответа:

Кастинг "почти тривиален" в том смысле, что он только проверяет, что данный объект расширяет некоторый класс или реализует интерфейс - он не изменяет сам объект.

В вашем случае, если func обозначает лямбда-выражение или ссылку на метод

executor.execute((Runnable & Serializable) () -> System.out.println("5"));
executor.execute((Runnable & Serializable) System.out::println);

LambdaMetafactory гарантирует, что сгенерированный лямбда-объект действительно реализует Runnable и Serializable, и приведение может даже быть оптимизировано.

Если, однако, func является параметром для вашего метод:

public void execute(Runnable func) {
    executor.execute((Runnable & Serializable)func);
}

Ни компилятор java, ни среда выполнения java каким-то волшебным образом не сделают func Сериализуемым.

В этом случае вы можете переписать свой метод как

public <T extends Runnable & Serializable> void execute(T func) {
    executor.execute(func);
}

Который требует, чтобы вызывающий объект предоставил запускаемый и сериализуемый объект - либо автоматически созданный (через лямбда-выражение или ссылку на метод), либо "вручную" закодированный класс.

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

public interface RunnableSerializable extends Runnable, Serializable {
     // override and add as necessary
}

public class MyRunnableClass implementes RunnableSerializable {
    // your runnable code
}

MyRunnableClass clazz = ...
executor.execute(clazz);

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

Приведение само по себе очень тривиально: это просто if-else, который будет оптимизирован JIT и предсказателем ветвей, потому что вы все время передаете допустимые классы (Вы не видите никаких ClassCastException).

Я думаю, что реальный вопрос здесь, Если есть какая-либо разница между выполнением (путем создания виртуального вызова через интерфейс через Runnable.run ()) анонимный лямбда-код или объявленный класс. Поэтому я установил тестовый тест JMH для тестирования 3 следующих случаев с использованием лямбд и объявил классы:

  1. выполнить материал
  2. выполнять произвольно разные вещи (чтобы предотвратить предсказание ветвей, если таковые имеются)
  3. выполнять произвольно разные вещи, но некоторые вещи будут происходить чаще (чтобы позволить предсказание ветвей, если таковые имеются)

Результат показывает, что лямбды медленнее на несколько наносекунд, поэтому на самом деле нет никакой разницы между labmda или объявленным классом вообще:

    Benchmark                                                                                Mode      Cnt         Score    Error  Units
    MyBenchmark.testAnonymousLambda                                                        sample  7272891        16.150 ±  1.646  ns/op
    MyBenchmark.testDeclared                                                               sample  7401499        15.349 ±  3.648  ns/op
    MyBenchmark.testRandomAnonymousLambda                                                  sample  6851255      3314.151 ± 11.655  ns/op
    MyBenchmark.testRandomBranchingDeclared                                                sample  6887926      3293.818 ±  9.180  ns/op
    MyBenchmark.testPredictableAnonymousLambda                                             sample  3990711      6091.766 ± 25.912  ns/op
    MyBenchmark.testPredictableBranchingDeclared                                           sample  3993885      6055.421 ± 21.535  ns/op

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

С. П. контрольный код доступен через суть