#java #lambda #variadic-functions
#java #лямбда #переменные-функции
Вопрос:
Я пытаюсь объединить лямбды и простые значения в varag.
public static void Log(String format, Object ... args) {
final Object[] fmt = new Object[ args.length ];
for(int i = 0; i < args.length; i )
fmt[i] = args[i] instanceof Supplier ?
( (Supplier) args[i] ).get() :
args[i];
final String s = String.format( format, fmt );
System.err.println( s );
}
final Supplier
s = () -> "aaa",
d = () -> 111;
Log( "%s %d %s %d", "bbb", 222, s, d ); // OK, OUTPUT: bbb 222 aaa 111
Log( "%s %d %s %d", "bbb", 222, () -> "aaa", () -> 111 ); // COMPILE FAIL
ОШИБКА: метод Log не может быть применен к заданным типам; НАЙДЕНА ТРЕБУЕМАЯ строка, объект[]: String,String,int,()-> «aaa»,()-> 111 ПРИЧИНА: несоответствие переменных; Объект не является функциональным интерфейсом
Возможно ли передать как лямбды, так и значения в vararg?
Комментарии:
1. переменные должны иметь один и тот же тип, поэтому попробуйте это:
Log( "%s %d %s %d",(Object) "bbb", (Object) 222,(Object) () -> "aaa",(Object) () -> 111 );
2. @ Timothy Truckle Это решение все равно выдаст ошибку компилятора.
3. Я пробовал (Object), но это не помогает. (Поставщик) кастинг решил проблему.
Ответ №1:
Проблема заключается в сообщении об ошибке
Объект не является функциональным интерфейсом
Вы можете создать лямбда-выражение только для функциональных интерфейсов (один с ровно одним абстрактным методом) Объект не является интерфейсом и не имеет никаких абстрактных методов, поэтому вы не можете создать лямбда-выражение этого типа. Что вы можете сделать, так это
Log( "%s %d %s %d", "bbb", 222, (Supplier) () -> "aaa", (Supplier) () -> 111 );
Таким образом, компилятор знает, какой тип лямбды вы намеревались реализовать.
Для сравнения вы могли бы написать следующее, и это будет вести себя по-другому в вашем методе.
Log( "%s %d %s %d", "bbb", 222, (Callable) () -> "aaa", (Callable) () -> 111 );
Ответ №2:
Для компилятора нет способа определить, какой тип функционального интерфейса используется при их удалении (в отличие от первого, поскольку вы определили это в variableType)
Таким образом, исправлением будет кастинг поставщика. Например. (не проверено)
Log( "%s %d %s %d", "bbb", 222, ((Supplier<String>)() -> "aaa"), ((Suplier<Integer>)() -> 111) );
Надеюсь, это указывает в правильном направлении.
Ответ №3:
Проблема в том, что Object
это не a @FunctionalInterface
. При этом вы можете передать простой анонимный экземпляр следующим образом:
Log( "%s %d %s %d", "bbb", 222, new Supplier<String>() {
@Override
public String get() {
return "aaa";
}
});
Этот метод можно использовать, если вы не хотите использовать непроверенные приведения, которые приведут к предупреждению компилятора.
Если вы все еще хотите использовать свою лямбду, это можно сделать следующим образом:
Log( "%s %d %s %d", "bbb", 222, (Supplier<String>) () -> "aaa");
Комментарии:
1. Мой ответ является альтернативой вашему.
2. В чем проблема с этим? Лямбды — это синтаксический сахар для анонимных внутренних классов
@FunctionalInterface
, аннотированных с.