Argumen Java 8 lambda Void

188

Katakanlah saya memiliki antarmuka fungsional berikut di Java 8:

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

Dan untuk beberapa kasus saya perlu tindakan tanpa argumen atau tipe pengembalian. Jadi saya menulis sesuatu seperti ini:

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

Namun, itu memberi saya kesalahan kompilasi, saya harus menuliskannya sebagai

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

Itu jelek. Apakah ada cara untuk menghilangkan Voidparameter type?

Wickoo
sumber
1
Lihatlah stackoverflow.com/questions/14319787/…
BobTheBuilder
7
Jika Anda memerlukan tindakan, seperti yang Anda tentukan, itu tidak mungkin. Namun, contoh pertama Anda bisa cocok dengan Runnable, yang Anda cariRunnable r = () -> System.out.println("Do nothing!");
Alexis C.
1
@ BobTheBuilder Saya tidak ingin menggunakan Konsumen seperti yang disarankan dalam posting itu.
Wickoo
2
Jawaban Matt membuat tipenya bekerja, tetapi apa yang dilakukan penelepon ketika mendapat nilai balik nol?
Stuart Marks
8
Anda dapat saling menyalahkan dan berharap bahwa saran 2 & 3 di pos ini diterima untuk Java 9!
assylias

Jawaban:

110

Sintaks yang Anda cari dimungkinkan dengan fungsi pembantu kecil yang mengubah a Runnablemenjadi Action<Void, Void>(misalnya, Anda dapat menempatkannya Action):

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"));
Mat
sumber
4
Ini adalah solusi terbersih yang bisa Anda dapatkan, IMO, jadi +1 (atau dengan metode statis di antarmuka itu sendiri)
Alexis C.
Solusi Konstantin Yovkov di bawah ini (dengan @FunctionalInterface) adalah solusi yang lebih baik, karena tidak melibatkan obat generik dan tidak memerlukan kode tambahan.
uthomas
@ Thomas Maaf, saya tidak melihat jawaban yang melibatkan @FunctionalInterface. Dia hanya mengatakan, bahwa tidak mungkin untuk memperpanjangnya ...
Mat
1
Hai @Matt, maaf. Saya bereaksi terlalu cepat. Untuk pertanyaan yang diberikan, jawaban Anda benar-benar valid. Sayangnya, suara saya terkunci, jadi saya tidak dapat menghapus -1 pada jawaban ini. Dua catatan: 1. Alih-alih Runnabletindakan harus mengambil @FunctionalInterfacesesuatu yang disebut kebiasaan SideEffect, 2. kebutuhan untuk fungsi pembantu seperti itu menyoroti bahwa sesuatu yang aneh sedang terjadi dan mungkin abstraksi rusak.
uthomas
530

Gunakan Supplierjika tidak mengambil apa-apa, tetapi mengembalikan sesuatu.

Gunakan Consumerjika dibutuhkan sesuatu, tetapi tidak menghasilkan apa-apa.

Gunakan Callablejika itu mengembalikan hasil dan mungkin melempar (paling mirip dengan Thunkistilah CS umum).

Gunakan Runnablejika tidak dan tidak bisa melempar.

x1a0
sumber
Sebagai contoh saya melakukan ini untuk membungkus "kekosongan" panggilan kembali: public static void wrapCall(Runnable r) { r.run(); }. Terima kasih
Maxence
13
jawaban yang indah. Singkat dan tepat.
Clint Eastwood
Sayangnya, tidak membantu jika harus membuang pengecualian yang diperiksa.
Jesse Glick
13
Sebagai penyelesaian untuk jawaban ini, yang tidak akan layak untuk diedit: Anda juga dapat menggunakan BiConsumer (mengambil 2, mengembalikan 0), Fungsi (mengambil 1, mengembalikan 1) dan BiFunction (mengambil 2, mengembalikan 1). Itu adalah yang paling penting untuk diketahui
CLOVIS
2
Apakah ada sesuatu seperti Callable (yang melempar pengecualian dalam metode call ()) tetapi apakah memerlukan nilai pengembalian?
dpelisek
40

Lambda:

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

sebenarnya mewakili implementasi untuk antarmuka seperti:

public interface Something {
    void action();
}

yang sama sekali berbeda dari yang telah Anda tetapkan. Itu sebabnya Anda mendapatkan kesalahan.

Karena Anda tidak dapat memperpanjang @FunctionalInterface, atau memperkenalkan yang baru, maka saya pikir Anda tidak memiliki banyak pilihan. Anda dapat menggunakan Optional<T>antarmuka untuk menunjukkan bahwa beberapa nilai (tipe kembali atau parameter metode) tidak ada. Namun, ini tidak akan membuat tubuh lambda lebih sederhana.

Konstantin Yovkov
sumber
Masalahnya adalah bahwa Somethingfungsi Anda tidak dapat menjadi subtipe dari Actiontipe saya , dan saya tidak dapat memiliki dua tipe yang berbeda.
Wickoo
Secara teknis dia bisa, tapi dia bilang dia ingin menghindarinya. :)
Konstantin Yovkov
31

Anda dapat membuat sub-antarmuka untuk kasus khusus itu:

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

Ini menggunakan metode default untuk mengganti metode parameterisasi yang diwariskan Void execute(Void), mendelegasikan panggilan ke metode yang lebih sederhana void execute().

Hasilnya adalah lebih mudah digunakan:

Command c = () -> System.out.println("Do nothing!");
Jordão
sumber
Dari mana Aksi <Void, Void> ini berasal? Baik antarmuka Swing maupun JAX-WX Action tidak memiliki antarmuka generik seperti itu?
luis.espinal
1
@ luis.espinal: Action<T, U>dideklarasikan dalam pertanyaan .....
Jordão
Hahaha, bagaimana sih aku merindukan itu? Terima kasih!
luis.espinal
6

Saya pikir tabel ini pendek dan bermanfaat:

Supplier       ()    -> x
Consumer       x     -> ()
Callable       ()    -> x throws ex
Runnable       ()    -> ()
Function       x     -> y
BiFunction     x,y   -> z
Predicate      x     -> boolean
UnaryOperator  x1    -> x2
BinaryOperator x1,x2 -> x3

Seperti yang dikatakan pada jawaban lain, opsi yang sesuai untuk masalah ini adalah a Runnable

GabrielRado
sumber
5

Itu tidak mungkin. Fungsi yang memiliki tipe pengembalian non-void (bahkan jika itu Void) harus mengembalikan nilai. Namun Anda dapat menambahkan metode statis Actionyang memungkinkan Anda untuk "membuat" a 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;
   } 
}

Itu akan memungkinkan Anda untuk menulis yang berikut:

// 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));
fabian
sumber
4

Tambahkan metode statis di dalam antarmuka fungsional Anda

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);
    }

}

Keluaran

Do nothing!
MCHAppy
sumber
3

Saya tidak berpikir itu mungkin, karena definisi fungsi tidak cocok dalam contoh Anda.

Ekspresi lambda Anda dievaluasi persis seperti

void action() { }

sedangkan deklarasi Anda terlihat seperti

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

sebagai contoh, jika Anda memiliki antarmuka berikut

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

satu-satunya jenis fungsi (saat instantiating) yang akan kompatibel terlihat seperti

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

dan salah satu dari pernyataan atau argumen pengembalian akan memberi Anda kesalahan kompiler.

Oleh karena itu, jika Anda mendeklarasikan fungsi yang mengambil argumen dan mengembalikan satu, saya pikir tidak mungkin untuk mengubahnya menjadi fungsi yang tidak disebutkan di atas.

pnadczuk
sumber
3

Hanya untuk referensi antarmuka fungsional yang dapat digunakan untuk referensi metode dalam kasus metode melempar dan / atau mengembalikan nilai.

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
}
SlavaL
sumber
Harap tambahkan beberapa konteks lagi. Ini terlihat menarik, tetapi artinya tidak segera jelas.
bnieland