Java 8 lambda Void โต้แย้ง


188

สมมติว่าฉันมีส่วนต่อประสานการทำงานใน Java 8:

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

และสำหรับบางกรณีฉันต้องการการดำเนินการโดยไม่มีข้อโต้แย้งหรือประเภทการคืนสินค้า ดังนั้นฉันจึงเขียนสิ่งนี้:

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

อย่างไรก็ตามมันทำให้ฉันรวบรวมข้อผิดพลาดฉันต้องเขียนมันเป็น

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

ซึ่งน่าเกลียด มีวิธีกำจัดVoidพารามิเตอร์ประเภทหรือไม่



7
หากคุณต้องการดำเนินการตามที่คุณกำหนดมันเป็นไปไม่ได้ อย่างไรก็ตามตัวอย่างแรกของคุณอาจเหมาะสมกับ a Runnableซึ่งคุณกำลังมองหาRunnable r = () -> System.out.println("Do nothing!");
อเล็กซิสซี

1
@BobTheBuilder ฉันไม่ต้องการใช้ Consumer ตามที่แนะนำในโพสต์นั้น
Wickoo

2
คำตอบของ Matt ทำให้ประเภทใช้งานได้ แต่ผู้โทรทำอะไรเมื่อได้รับค่าคืน null
Stuart Marks

8
คุณสามารถข้ามนิ้วและหวังว่าคำแนะนำ 2 & 3 ในโพสต์นี้จะได้รับการยอมรับสำหรับ Java 9!
assylias

คำตอบ:


110

ไวยากรณ์คุณหลังจากที่เป็นไปได้ด้วยฟังก์ชั่นช่วยตัวน้อยที่แปลงRunnableลงในAction<Void, Void>(คุณสามารถวางไว้ใน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"));

4
นี่เป็นวิธีแก้ปัญหาที่สะอาดที่สุดที่คุณจะได้รับ IMO ดังนั้น +1 (หรือด้วยวิธีการคงที่ในอินเทอร์เฟซของตัวเอง)
อเล็กซิสซี

โซลูชันของ Konstantin Yovkov ด้านล่าง (พร้อม @FunctionalInterface) เป็นโซลูชันที่ดีกว่าเนื่องจากไม่เกี่ยวข้องกับข้อมูลทั่วไปและไม่จำเป็นต้องใช้รหัสเพิ่มเติม
uthomas

@uthomas @FunctionalInterfaceขออภัยผมไม่เห็นคำตอบที่เกี่ยวข้องกับ เขาแค่บอกว่ามันเป็นไปไม่ได้ที่จะขยายมันออกไป ...
Matt

1
สวัสดี @Matt ขอโทษ ฉันตอบสนองเร็วเกินไป สำหรับคำถามที่คุณตอบถูกต้องสมบูรณ์ น่าเสียดายที่การลงคะแนนของฉันถูกล็อคดังนั้นฉันจึงไม่สามารถลบ -1 ในคำตอบนี้ได้ หมายเหตุสองประการ: 1. แทนที่จะRunnableดำเนินการควรใช้@FunctionalInterfaceสิ่งที่กำหนดเองที่เรียกว่าSideEffect2. ความต้องการฟังก์ชั่นตัวช่วยดังกล่าวเน้นให้เห็นว่ามีบางสิ่งแปลก ๆ เกิดขึ้นและอาจเป็นไปได้ว่า
uthomas

530

ใช้Supplierถ้าไม่ทำอะไรเลย แต่ส่งคืนบางอย่าง

ใช้Consumerถ้ามันใช้บางอย่าง แต่ไม่คืนสิ่งใดเลย

ใช้Callableถ้ามันส่งกลับผลลัพธ์และอาจโยน (คล้ายกับThunkข้อกำหนด CS ทั่วไป)

ใช้Runnableถ้ามันไม่และไม่สามารถโยน


เป็นตัวอย่างที่ผมทำอย่างนี้กับห่อ "โมฆะ" public static void wrapCall(Runnable r) { r.run(); }โทรกลับมา: ขอบคุณ
Maxence

13
คำตอบที่สวยงาม สั้นและแม่นยำ
Clint Eastwood

ไม่ช่วยถ้ามันต้องโยนยกเว้นการตรวจสอบโชคไม่ดี
Jesse Glick

13
เนื่องจากเสร็จสิ้นคำตอบนี้ซึ่งจะไม่คุ้มค่าการแก้ไข: คุณยังสามารถใช้ BiConsumer (ใช้เวลา 2 ส่งกลับ 0) ฟังก์ชั่น (ใช้เวลา 1 ส่งกลับ 1) และฟังก์ชั่น BiFunction (ใช้เวลา 2 ส่งกลับ 1) สิ่งเหล่านี้สำคัญที่สุดที่ต้องรู้
CLOVIS

2
มีสิ่งใดที่เหมือนกับ Callable (ซึ่งมีข้อยกเว้นในวิธีการโทร ()) แต่ไม่ต้องการค่าส่งคืนหรือไม่?
dpelisek

40

แลมบ์ดา:

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

จริง ๆ แล้วหมายถึงการใช้งานสำหรับอินเทอร์เฟซเช่น:

public interface Something {
    void action();
}

ซึ่งแตกต่างจากที่คุณกำหนดไว้อย่างสิ้นเชิง นั่นเป็นเหตุผลที่คุณได้รับข้อผิดพลาด

เนื่องจากคุณไม่สามารถขยาย@FunctionalInterfaceหรือแนะนำแบรนด์ใหม่ฉันคิดว่าคุณไม่มีตัวเลือกมากมาย คุณสามารถใช้Optional<T>อินเทอร์เฟซเพื่อแสดงว่าค่าบางอย่าง (ชนิดส่งคืนหรือพารามิเตอร์วิธี) หายไป อย่างไรก็ตามสิ่งนี้จะไม่ทำให้ตัวแลมบ์ดาง่ายขึ้น


ปัญหาคือว่าการSomethingทำงานของคุณไม่สามารถเป็นประเภทย่อยของฉันActionและฉันไม่สามารถมีสองประเภทที่แตกต่างกัน
Wickoo

เขาสามารถใช้เทคโนโลยีได้ แต่เขาบอกว่าเขาต้องการหลีกเลี่ยง :)
Konstantin Yovkov

31

คุณสามารถสร้างอินเตอร์เฟสย่อยสำหรับกรณีพิเศษ:

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

มันใช้วิธีการเริ่มต้นเพื่อแทนที่วิธีการแปรพารามิเตอร์รับVoid execute(Void)มอบสิทธิ์การโทรไปยังวิธีที่ง่ายvoid execute()ขึ้น

ผลลัพธ์คือใช้ง่ายกว่ามาก:

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

Action <Void, Void> มาจากไหน อินเตอร์เฟส Swing หรือ JAX-WX Action ไม่มีอินเตอร์เฟสทั่วไปเช่นนั้นหรือไม่?
luis.espinal

1
@ luis.espinal: Action<T, U>ถูกประกาศในคำถาม .....
Jordão

ฮ่าฮ่าฮ่าห่าฉันพลาดได้ยังไง? ขอบคุณ!
luis.espinal

6

ฉันคิดว่าตารางนี้สั้นและมีประโยชน์:

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

ตามที่กล่าวไว้ในคำตอบอื่น ๆ ตัวเลือกที่เหมาะสมสำหรับปัญหานี้คือ Runnable


5

ที่เป็นไปไม่ได้ ฟังก์ชั่นที่มีประเภทผลตอบแทนที่ไม่เป็นโมฆะ (แม้ว่ามันจะVoid) จะต้องส่งกลับค่า อย่างไรก็ตามคุณสามารถเพิ่มวิธีการคงที่เพื่อActionให้คุณ "สร้าง" 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;
   } 
}

ซึ่งจะช่วยให้คุณสามารถเขียนสิ่งต่อไปนี้:

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

เพิ่มวิธีการคงที่ภายในส่วนต่อประสานการทำงานของคุณ

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

}

เอาท์พุต

Do nothing!

3

ฉันไม่คิดว่ามันเป็นไปได้เพราะคำจำกัดความของฟังก์ชั่นไม่ตรงกันในตัวอย่างของคุณ

การแสดงออกแลมบ์ดาของคุณจะได้รับการประเมินตาม

void action() { }

ในขณะที่การประกาศของคุณดูเหมือน

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

ตัวอย่างเช่นถ้าคุณมีอินเตอร์เฟซดังต่อไปนี้

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

ฟังก์ชั่นชนิดเดียว (ในขณะที่สร้างอินสแตนซ์) ที่จะเข้ากันได้ดูเหมือนว่า

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

และการขาดคำสั่งส่งคืนหรืออาร์กิวเมนต์จะทำให้เกิดข้อผิดพลาดของคอมไพเลอร์

ดังนั้นหากคุณประกาศฟังก์ชั่นที่รับอาร์กิวเมนต์และส่งคืนหนึ่งฉันคิดว่ามันเป็นไปไม่ได้ที่จะแปลงเป็นฟังก์ชันที่ไม่ได้กล่าวถึงข้างต้น


3

สำหรับการอ้างอิงว่าอินเตอร์เฟสการทำงานใดที่สามารถใช้สำหรับการอ้างอิงเมธอดในกรณีเมธอด throws และ / หรือคืนค่า

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
}

โปรดเพิ่มบริบทเพิ่มเติม สิ่งนี้ดูน่าสนใจ แต่ความหมายของมันไม่ชัดเจนในทันที
bnieland
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.