Java 8 lambda Void argümanı


188

Diyelim ki Java 8'de aşağıdaki işlevsel arayüze sahibim:

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

Ve bazı durumlarda argüman veya dönüş türü olmayan bir eyleme ihtiyacım var. Yani böyle bir şey yazıyorum:

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

Ancak, bana derleme hatası veriyor, bunu şöyle yazmam gerekiyor

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

Hangi çirkin. VoidType parametresinden kurtulmanın bir yolu var mı ?



7
Tanımladığınız gibi bir eyleme ihtiyacınız varsa, bu mümkün değildir. Ancak, ilk örneğiniz Runnable, aradığınız şeyle uyumlu olabilirRunnable r = () -> System.out.println("Do nothing!");
Alexis C.

1
@BobTheBuilder Bu yayında önerildiği gibi bir Tüketici kullanmak istemiyorum.
Wickoo

2
Matt'in cevabı türlerin çalışmasını sağlar, ancak arayan bir boş dönüş değeri aldığında ne yapar?
Stuart Marks

8
Parmakları geçebilir ve bu yayındaki 2 ve 3 numaralı önerilerin Java 9 için kabul edilmesini umabilirsiniz !
assylias

Yanıtlar:


110

Peşinde olduğun sözdizimi din değiştirenler bu biraz yardımcı fonksiyonu ile mümkündür Runnableiçine Action<Void, Void>(eğer yerleştirebilirsiniz Actionörneğin):

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

4
Bu alabileceğiniz en temiz geçici çözümdür, IMO, yani +1 (veya arayüzün kendisinde statik bir yöntemle)
Alexis C.

Konstantin Yovkov'un aşağıdaki çözümü (@FunctionalInterface ile) daha iyi bir çözümdür, çünkü jenerik içermez ve ekstra kod gerektirmez.
uthomas

@uthomas Üzgünüm, ben bir cevap görmüyorum @FunctionalInterface. O sadece, uzatmak mümkün olmadığını söylüyor ...
Matt

1
Merhaba @ Matt, üzgünüm. Çok hızlı tepki verdim. Verilen soru için cevapladığınız soru tamamen geçerlidir. Maalesef, oyum kilitlendi, bu yüzden -1'i bu cevaptan kaldıramıyorum. İki not: 1. RunnableEylem yerine özel bir @FunctionalInterfaceşey almalıdır SideEffect, 2. böyle bir yardımcı fonksiyon ihtiyacı garip bir şey oluyor ve muhtemelen soyutlama kırık olduğunu vurgulamaktadır.
uthomas

531

SupplierHiçbir şey almazsa, ancak bir şey döndürürse kullanın .

ConsumerBir şey alır, ancak hiçbir şey döndürmezse kullanın .

Kullanım Callablebir sonuç döndürür ve atmak if (en benzer Thunkgenel CS açısından).

RunnableAtmazsa ve alamazsa kullanın .


Örnek olarak ben bu sarmak için "boşluk" dönüş çağrısı yaptı: public static void wrapCall(Runnable r) { r.run(); }. Teşekkürler
Maxence

13
güzel cevap. Kısa ve hassas.
Clint Eastwood

Maalesef kontrol edilmiş bir istisna atması gerekiyorsa yardımcı olmaz.
Jesse Glick

13
Düzenlemeye değmeyecek bu cevap için tamamlama olarak: BiConsumer (2 alır, 0 döndürür), İşlev (1 alır, 1 döndürür) ve BiFunction (2 alır, 1 döndürür) kullanabilirsiniz.
Bunları

2
Callable (call () yönteminde istisna oluşturan) gibi bir şey var mı, ancak herhangi bir dönüş değeri gerektiriyor mu?
dpelisek

40

Lambda:

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

aslında aşağıdaki gibi bir arayüz için bir uygulamayı temsil eder:

public interface Something {
    void action();
}

tanımladığınızdan tamamen farklı. Bu yüzden bir hata alıyorsunuz.

@FunctionalInterfaceNe yeni ne de yepyeni bir tanıtmak beri , o zaman çok seçenek yok sanırım. Optional<T>Yine de, bazı değerlerin (dönüş türü veya yöntem parametresi) eksik olduğunu belirtmek için arabirimleri kullanabilirsiniz . Bununla birlikte, bu lambda gövdesini basitleştirmeyecektir.


Sorun şu ki, Somethingfonksiyonunuz benim tipimin bir alt Actiontipi olamaz ve iki farklı tipim olamaz.
Wickoo

Teknik olarak yapabilir, ama bundan kaçınmak istediğini söyledi. :)
Konstantin Yovkov

31

Bu özel durum için bir alt arabirim oluşturabilirsiniz:

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

Daha basit yönteme çağrıyı devrederek devralınan parametreli yöntemi geçersiz kılmak için varsayılan bir yöntem kullanır .Void execute(Void)void execute()

Sonuç, kullanımı çok daha basittir:

Command c = () -> System.out.println("Do nothing!");

Bu <Boşluk, Boşluk> eylemi nereden geliyor? Ne Swing ne de JAX-WX Action arayüzleri böyle genel bir arayüze sahip değil mi?
luis.espinal

1
@ luis.espinal: soruda Action<T, U>açıklandı .....
Jordão

Hahaha, bunu nasıl özledim? Teşekkürler!
luis.espinal

6

Bu tablo kısa ve yararlı olduğunu düşünüyorum:

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

Diğer cevaplarda belirtildiği gibi, bu sorun için uygun seçenek Runnable


5

Bu imkansız. Geçersiz bir dönüş türüne sahip bir işlev (olsa bile Void) bir değer döndürmek zorundadır. Bununla birlikte Action, a "oluşturmanıza" izin veren statik yöntemler ekleyebilirsiniz 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;
   } 
}

Bu, aşağıdakileri yazmanıza izin verir:

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

4

İşlevsel arayüzünüze statik bir yöntem ekleyin

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

}

Çıktı

Do nothing!

3

Bunun mümkün olduğunu düşünmüyorum, çünkü işlev tanımları örneğinizde eşleşmiyor.

Lambda ifadeniz tam olarak şu şekilde değerlendirilir:

void action() { }

oysa beyanınız şöyle görünür

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

Örneğin, aşağıdaki arayüzünüz varsa

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

uyumlu olacak tek işlev (somutlaştırırken)

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

ve dönüş ifadesi veya argüman eksikliği size derleyici hatası verir.

Bu nedenle, bir argüman alan ve bir işlev döndüren bir işlev bildirirseniz, yukarıda belirtilenlerin hiçbirini yerine getirmeyen bir işleve dönüştürmenin imkansız olduğunu düşünüyorum.


3

Sadece referans için, yöntemin bir değer atması ve / veya döndürmesi durumunda yöntem referansı için hangi işlevsel arayüzün kullanılabileceği.

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
}

Lütfen biraz daha bağlam ekleyin. Bu ilginç görünüyor, ancak anlamı hemen belli değil.
bnieland
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.