Какой объект назначен функциональному интерфейсу в 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)
так как вы можете повторно использовать один и тот же объект, чтобы применить эту операцию с другим вводом.