Argumento void de Java 8 lambda


Digamos que tengo la siguiente interfaz funcional en Java 8:

interface Action<T, U> {
   U execute(T t);
}

Y para algunos casos necesito una acción sin argumentos o tipo de retorno. Así que escribo algo como esto:

Action<Void, Void> a = () -> { System.out.println("Do nothing!"); };

Sin embargo, me da error de compilación, necesito escribirlo como

Action<Void, Void> a = (Void v) -> { System.out.println("Do nothing!"); return null;};

Que es feo. ¿Hay alguna manera de deshacerse del parámetro tipo Void?

Author: rogerdpack, 2015-04-29

8 answers

La sintaxis que buscas es posible con una pequeña función auxiliar que convierte un Runnable en Action<Void, Void> (puedes colocarlo en Action por ejemplo):

public static Action<Void, Void> action(Runnable runnable) {
    return (v) -> {
        runnable.run();
        return null;
    };
}

// Somewhere else in your code
 Action<Void, Void> action = action(() -> System.out.println("foo"));
 82
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-29 14:15:30

Uso Supplier si se tarda nada.

Uso Consumer si no devuelve nada.

Uso Runnable si no lo hace.

 240
Author: x1a0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-12 02:08:51

La lambda:

() -> { System.out.println("Do nothing!"); };

En realidad representa una implementación para una interfaz como:

public interface Something {
    void action();
}

Que es completamente diferente a la que has definido. Es por eso que obtienes un error.

Dado que no puedes extender tu @FunctionalInterface, ni introducir uno nuevo, entonces creo que no tienes muchas opciones. Sin embargo, puede usar las interfaces Optional<T> para indicar que faltan algunos de los valores (tipo de retorno o parámetro de método). Sin embargo, esto no hará que el cuerpo lambda más simple.

 35
Author: Konstantin Yovkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-29 13:56:23

Puede crear una sub-interfaz para ese caso especial:

interface Command extends Action<Void, Void> {
  default Void execute(Void v) {
    execute();
    return null;
  }
  void execute();
}

Utiliza un método predeterminado para anular el método parametrizado heredado Void execute(Void), delegando la llamada al método más simple void execute().

El resultado es que es mucho más fácil de usar:

Command c = () -> System.out.println("Do nothing!");
 25
Author: Jordão,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-19 12:09:25

Eso no es posible. Una función que tiene un tipo de retorno no nulo (incluso si es Void) tiene que devolver un valor. Sin embargo, puede agregar métodos estáticos a Action que le permite "crear" un Action:

interface Action<T, U> {
   U execute(T t);

   public static Action<Void, Void> create(Runnable r) {
       return (t) -> {r.run(); return null;};
   }

   public static <T, U> Action<T, U> create(Action<T, U> action) {
       return action;
   } 
}

Eso te permitiría escribir lo siguiente:

// create action from Runnable
Action.create(()-> System.out.println("Hello World")).execute(null);
// create normal action
System.out.println(Action.create((Integer i) -> "number: " + i).execute(100));
 5
Author: fabian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-29 14:10:15

Agregue un método estático dentro de su interfaz funcional

package example;

interface Action<T, U> {
       U execute(T t);
       static  Action<Void,Void> invoke(Runnable runnable){
           return (v) -> {
               runnable.run();
                return null;
            };         
       }
    }

public class Lambda {


    public static void main(String[] args) {

        Action<Void, Void> a = Action.invoke(() -> System.out.println("Do nothing!"));
        Void t = null;
        a.execute(t);
    }

}

Salida

Do nothing!
 4
Author: MCHAppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-29 16:15:25

No creo que sea posible, porque las definiciones de función no coinciden en su ejemplo.

Su expresión lambda se evalúa exactamente como

void action() { }

Mientras que su declaración parece

Void action(Void v) {
    //must return Void type.
}

Como ejemplo, si tiene la siguiente interfaz

public interface VoidInterface {
    public Void action(Void v);
}

El único tipo de función (al instanciar) que será compatibile se parece a

new VoidInterface() {
    public Void action(Void v) {
        //do something
        return v;
    }
}

Y la falta de instrucción return o argumento le dará un error de compilador.

Por lo tanto, si declaras una función que toma un argumento y devuelve uno, creo que es imposible convertirlo en una función que no hace nada de lo mencionado anteriormente.

 3
Author: pnadczuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-29 14:03:40

Solo para referencia qué interfaz funcional se puede usar para la referencia del método en los casos en que el método arroja y/o devuelve un valor.

void notReturnsNotThrows() {};
void notReturnsThrows() throws Exception {}
String returnsNotThrows() { return ""; }
String returnsThrows() throws Exception { return ""; }

{
    Runnable r1 = this::notReturnsNotThrows; //ok
    Runnable r2 = this::notReturnsThrows; //error
    Runnable r3 = this::returnsNotThrows; //ok
    Runnable r4 = this::returnsThrows; //error

    Callable c1 = this::notReturnsNotThrows; //error
    Callable c2 = this::notReturnsThrows; //error
    Callable c3 = this::returnsNotThrows; //ok
    Callable c4 = this::returnsThrows; //ok

}


interface VoidCallableExtendsCallable extends Callable<Void> {
    @Override
    Void call() throws Exception;
}

interface VoidCallable {
    void call() throws Exception;
}

{
    VoidCallableExtendsCallable vcec1 = this::notReturnsNotThrows; //error
    VoidCallableExtendsCallable vcec2 = this::notReturnsThrows; //error
    VoidCallableExtendsCallable vcec3 = this::returnsNotThrows; //error
    VoidCallableExtendsCallable vcec4 = this::returnsThrows; //error

    VoidCallable vc1 = this::notReturnsNotThrows; //ok
    VoidCallable vc2 = this::notReturnsThrows; //ok
    VoidCallable vc3 = this::returnsNotThrows; //ok
    VoidCallable vc4 = this::returnsThrows; //ok
}
 3
Author: SlavaL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-03 11:06:42