Java vararg передает лямбда-выражение и значения

#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 , аннотированных с.