Какой объект назначен функциональному интерфейсу в Java?

Я новичок в лямбда-выражениях в Java, поэтому, пожалуйста, не реагируйте слишком резко, если этот вопрос кажется глупым: каков реальный объект, который будет реализован, если я назначу его FunctionalInterface? Т.е. если я сделаю List<String> lst = new ArrayList<>() Я реализую ArrayList объект. Какой объект создается, если я делаю Function<String,String> fct = s -> s? И является ли этот объект неизменным, т.е. имеет ли смысл иметь public static final Function<String,String> REUSE_OFTEN = s -> String.valueOf(s)?

2 ответа

Решение

Т.е., если я делаю List lst = new ArrayList<>(), я реализую объект ArrayList.

Вы создаете ArrayList скорее.

Какой объект создается, если я выполняю функцию fct = s -> s? И является ли этот объект неизменным, то есть имеет ли смысл иметь открытую статическую финальную функцию REUSE_OFTEN = s -> String.valueOf(s)?

Лямбды - это не то же самое, что анонимные классы с точки зрения байт-кода и с точки зрения поведения / функционирования. Этот последний достаточно близко, но не совсем то же самое.

Вот два метода, которые делают одно и то же: один с лямбдой, другой с анонимным классом:

public class CompareBoth {

    public void lambda() {
        Function<String,String> fct = s -> s;
        fct.apply("a");
    }


    public void anonymousClass() {
        Function<String,String> fct = new Function<String, String>() {

            @Override
            public String apply(String t) {
                return t;
            }

        };
        fct.apply("a");
    }
}

Вот разобранный код:

public class cli.CompareBoth {
  public cli.CompareBoth();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void lambda();
    Code:
       0: invokedynamic #2,  0              // InvokeDynamic #0:apply:()Ljava/util/function/Function;
       5: astore_1
       6: aload_1
       7: ldc           #3                  // String a
       9: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      14: pop
      15: return

  public void anonymousClass();
    Code:
       0: new           #5                  // class cli/CompareBoth$1
       3: dup
       4: aload_0
       5: invokespecial #6                  // Method cli/CompareBoth$1."<init>":(Lcli/CompareBoth;)V
       8: astore_1
       9: aload_1
      10: ldc           #3                  // String a
      12: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      17: pop
      18: return
}

Мы видим, что JVM относится к ним по-разному.

Каков фактический объект, который реализуется?

Когда создается лямбда, эта инструкция JVM выполняется:

0: invokedynamiC#2, 0 // InvokeDynamiC#0:apply:()Ljava/util/function/Function;

InvokeDynamic то, что было введено в Java 7, указывается как инструкция байт-кода, которая облегчает реализацию динамических языков (для JVM) посредством динамического вызова метода. Вы можете прочитать эту страницу javaworld, чтобы получить больше информации.
Теперь, действительно ли это имеет значение по мере вашего развития?
В большинстве случаев это не так.
Функциональные интерфейсы функционально близки к анонимным интерфейсам с небольшими отличиями.
Концептуально, я думаю, что это приемлемо рассматривать их таким образом.

О неизменяемости функционального интерфейса:

Функциональные интерфейсы могут иметь состояние, но предназначены для неизменяемости.

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

Когда вы пишете лямбда-выражение -

Function<String,String> fct = s -> s;

Он просто создает анонимный класс, который реализует Function<String, String> интерфейс и переопределяет его единственный метод apply() с реализацией, которую вы предоставили s -> s, И назначить этот объект Function<String, String> ссылка.

Function<String,String> fct = new Function<>() {
    @Override
    public String apply(String s) {
        return s;
    }
};

И это неизменный объект, и имеет смысл иметь public static final Function<String,String> REUSE_OFTEN = s -> String.valueOf(s) так как вы можете повторно использовать один и тот же объект, чтобы применить эту операцию с другим вводом.

Другие вопросы по тегам