เทียบเท่ากับ Func และ Action ของ Java


90

อะไรคือเทียบเท่า Java ของFuncและการดำเนินการ ?

ฉันหมายถึงแทนที่จะเขียนสิ่งนี้ด้วยตัวเอง:

public interface Func<TInput, TResult>
{
    TResult call(TInput target) throws Exception;
}
public interface Action<T>
{
    void call(T target) throws Exception;
}

คำตอบ:


100

ใน Java 8 สิ่งที่เทียบเท่าคืออินเทอร์เฟซjava.util.function.Function<T, R>และjava.util.function.Consumer<T>ตามลำดับ ในทำนองเดียวกันจะเทียบเท่ากับjava.util.function.Predicate<T> System.Predicate<T>ดังที่กล่าวไว้ในที่อื่นสิ่งเหล่านี้เป็นส่วนต่อประสานแทนการมอบหมาย

ที่เกี่ยวข้องกัน: ตอนนี้ฉันกำลังเอนเอียงอย่างมากกับคลาสยูทิลิตี้ต่อไปนี้เพื่อทำสิ่งต่างๆวิธีการขยายแบบ LINQ:

abstract class IterableUtil {
  public static <T> Iterable<T> where(Iterable<T> items, Predicate<T> predicate) {
    ArrayList<T> result = new ArrayList<T>();
    for (T item : items) {
      if (predicate.test(item)) {
        result.add(item);
      }
    }
    return result;
  }

  public static <T, R> Iterable<R> select(Iterable<T> items, Function<T, R> func) {
    ArrayList<R> result = new ArrayList<R>();
    for (T item : items) {
      result.add(func.apply(item));
    }
    return result;
  }
}

วิธีการที่แตกต่างSystem.Linq.Enumerable.Where<TSource>และเหมือนSystem.Linq.Enumerable.Select<TSource, TResult>LINQ ที่ฉันนำเสนอที่นี่ไม่ได้ขี้เกียจและสำรวจคอลเลกชันต้นทางอย่างเต็มที่ก่อนที่จะส่งคืนคอลเล็กชันผลลัพธ์ไปยังผู้โทร ถึงกระนั้นฉันพบว่ามันมีประโยชน์สำหรับจุดประสงค์ทางวากยสัมพันธ์ล้วนๆและอาจทำให้ขี้เกียจได้หากจำเป็น ให้

class Widget {
  public String name() { /* ... */ }
}

หนึ่งสามารถทำสิ่งต่อไปนี้:

List<Widget> widgets = /* ... */;
Iterable<Widget> filteredWidgets = IterableUtil.where(widgets, w -> w.name().startsWith("some-prefix"));

ที่ฉันชอบต่อไปนี้:

List<Widget> widgets = /* ... */;
List<Widget> filteredWidgets = new ArrayList<Widget>();
for (Widget w : widgets) {
  if (w.name().startsWith("some-prefix")) {
    filteredWidgets.add(w);
  }
}

1
เราจำเป็นต้องโหวตคำตอบนี้เป็นอย่างยิ่งเนื่องจากคำถามนี้เป็นผลการค้นหา "Java equivalent of action" ในปัจจุบันและตอนนี้ก็คือปี 2015 ดังนั้นสิ่งที่ Java 8 จึงดีกว่าสิ่งที่ Java เคยมีมาก่อนและเกือบจะเลียนแบบของ. net จุด.
Robert C.Barth

1
ฉันคิดว่าคุณหมายถึง Iterable <idget> filteredWidgets = IterableUtil.where (widgets, w -> w.name (). startWith ("some-prefix"));
ไฟฟ้าบาห์

1
นอกจากของFunction<T, R>และConsumer<T>คุณสามารถหาชุดเต็มของการเชื่อมต่อการทำงานร่วมกันว่า Java ให้ที่นี่
Jämes

36

อินเตอร์เฟซที่เรียกได้จะคล้ายกับ Func

อินเทอร์เฟซที่รันได้คล้ายกับ Action

โดยทั่วไป Java ใช้คลาสภายในที่ไม่ระบุชื่อแทนผู้แทน C # ตัวอย่างเช่นนี่คือวิธีที่คุณเพิ่มรหัสเพื่อตอบสนองต่อการกดปุ่มใน GUI:

button.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) { 
          ...//code that reacts to the action... 
      }
});

13
สิ่งที่ทำให้ Func แตกต่างจาก Callable คือมีโอเวอร์โหลดทั่วไปสำหรับ 16 อาร์กิวเมนต์ (Func <TResult>, Func <T, TResult>, Func <T1, T2, TResult> ฯลฯ ) OTOH Callable ไม่มีข้อโต้แย้ง ยิ่งไปกว่านั้นมันเป็นไปไม่ได้ที่จะใช้การโอเวอร์โหลดของ C # เนื่องจากการลบประเภทในยาสามัญ
Aleksandr Dubinsky

6

ความสง่างามของผู้ได้รับมอบหมายมากเกินไป Func (นอกเหนือจากผู้ร่วมประชุม VS ปัญหาระดับที่ไม่ระบุชื่อ) คือการที่พวกเขาสนับสนุน 0-16 ข้อโต้แย้ง ( Func<TResult>, Func<T, TResult>, Func<T1, T2, TResult>ฯลฯ )

น่าเสียดายที่สิ่งนี้เป็นไปไม่ได้ใน Java เนื่องจากการลบประเภท คลาสไม่สามารถแตกต่างกันตามพารามิเตอร์ประเภททั่วไปเพียงอย่างเดียว

Java 8 ตอนนี้นำในสวนสัตว์ของชื่อเช่นBiConsumerสำหรับAction<T, T2>และเพราะ Java BiIntConsumerไม่อนุญาตให้มีการขัดแย้งชนิดดั้งเดิม แม้ว่า "สวนสัตว์" จะไม่ใหญ่มากนักและฉันไม่ทราบว่ามีห้องสมุดที่ขยายขนาดนี้ มีข้อเสนอที่ยอดเยี่ยมสำหรับตัวอักษรประเภทฟังก์ชันเช่น(int, int) => voidแต่ไม่ได้นำมาใช้


3
ที่น่าสนใจในคลาสระดับ CLR ซึ่งแตกต่างกันตามจำนวนพารามิเตอร์ทั่วไปเท่านั้นที่มีชื่อต่างกัน Func`1ฯลฯ เป็นเพียง C # ที่จับคู่ชื่อเดียวกัน
CodesInChaos

@CodesInChaos อ่าน่าสนใจมาก น่าเสียดายที่ Java ไม่ได้ทำแบบนี้เช่นกัน Btw, Java 8 ตอนนี้นำในสวนสัตว์ของชื่อเช่นBiConsumerสำหรับAction<T, T2>และเพราะ Java BiIntConsumerไม่อนุญาตให้ใช้พารามิเตอร์ชนิดดั้งเดิม มีข้อเสนอสำหรับลิเทอรัลประเภทฟังก์ชันเหมือน(int, int) => voidแต่ไม่มีการนำมาใช้
Aleksandr Dubinsky

5

สำหรับการFunc<T>ใช้งาน: java.util.function.Supplier http://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html


Supplierจะเทียบเท่ากับFunc<T>(เมื่อเทียบกับFunc<T1, T2>) Actionไม่ได้ Actionยอมรับไม่มีข้อโต้แย้งและส่งกลับไม่มีผล (เวอร์ชันอื่นActionยอมรับอาร์กิวเมนต์จำนวนมากและไม่ส่งคืนผลลัพธ์)
Servy

ใช่คุณพูดถูกความผิดพลาดของฉัน ฉันพบโพสต์นี้เพราะฉันกำลังมองหาFunc<T>Java และจำผิดว่าเป็นAction<T>. อ๊ะ
Tim Schruben

คำตอบนั้นมีประโยชน์สำหรับฉันอยู่แล้ว java ยังมีสิ่งที่ต้องการAction<>: 0 อินพุต 0 เอาต์พุต ด้วย.andThen(...)ฟังก์ชันการทำงานที่ดีที่สุด
Kolya Ivankov

ฉันไม่ทราบว่ามีอะไรให้โดยกรอบงาน Java ที่เหมือนกับAction<>0 อินพุตและ 0 เอาต์พุต แต่จำใน Java สิ่งเหล่านี้เป็นเพียงอินเทอร์เฟซ ดังนั้นคุณสามารถสร้างของคุณเองเพื่อใช้
Tim Schruben

มีRunnableไว้สำหรับAction<>แม้ว่ามันจะไม่ค่อยสวยเท่าที่จะใช้กับสิ่งที่ใช้งานได้ Java 8 ใหม่
Mark K Cowan

3

ไม่มีสิ่งใดเทียบเท่ากับสิ่งเหล่านี้ คุณสามารถสร้างคลาสภายในที่ไม่ระบุชื่อใน Java ได้ แต่มีแนวโน้มที่จะเป็นอินเทอร์เฟซเฉพาะแทนที่จะเป็นแบบทั่วไปเช่น Func และ Action


3

Java ไม่มีแนวคิดของผู้รับมอบสิทธิ์ สำหรับวิธีการแก้ปัญหาโปรดดูที่โปรแกรมเมอร์ Java ดูที่ C # Delegates :

ในขณะที่ C # มีชุดความสามารถคล้ายกับ Java แต่ได้เพิ่มคุณสมบัติใหม่และน่าสนใจหลายอย่าง การมอบหมายคือความสามารถในการปฏิบัติต่อเมธอดเป็นอ็อบเจ็กต์ชั้นหนึ่ง AC # delegate ใช้โดยที่นักพัฒนา Java จะใช้อินเทอร์เฟซด้วยวิธีการเดียว ในบทความนี้จะกล่าวถึงการใช้ผู้รับมอบสิทธิ์ใน C # และโค้ดจะถูกนำเสนอสำหรับอ็อบเจ็กต์ Java Delegate ที่สามารถทำหน้าที่คล้ายกันได้ ดาวน์โหลดซอร์สโค้ดที่นี่


3

คุณสามารถใช้java.util.Functionแบบนี้

Function<Employee, String> f0 = (e) -> e.toString();

แต่ถ้าคุณจะใช้กับอาร์กิวเมนต์มากกว่าหนึ่งอาร์กิวเมนต์ (เช่นเดียวกับ C # Func) คุณจะต้องกำหนดเวอร์ชัน FunctionalInterface ของคุณดังนี้

@FunctionalInterface
public interface Func2Args<T, T1, R> {
    R apply(T t, T1 t1);
}

@FunctionalInterface
public interface Func3Args<T,T1,T2,R> {
    R apply(T t, T1 t1, T2 t2);
}

จากนั้นคุณสามารถใช้กับตัวแปร no ของอาร์กิวเมนต์

Func2Args<Employee,Employee,String> f2 = (e, e2) -> e.toString() + 
e2.toString();

Func3Args<Employee,Employee,Employee,String> f3 = (e, e2, e3) -> 
e.toString() + e2.toString() + e3.toString();

1

สำหรับเวอร์ชันเก่ากว่า Java 8

สำหรับวิธีการโทรกลับใน C # ซึ่งฉันใช้แบบนี้:

public void MyMethod(string par1, string par2, Action<int> callback, Action<int, string> callback2)
{
    //Async Code 
        callback.invoke(1);
        callback2.invoke(4, "str");
}

และเรียกมันว่า:

utils.MyMethod("par1", "par2", (i) =>
{
    //cb result
}, (i, str) =>
{
    //cb2 result
});

ฉันได้สร้างคลาสนามธรรมขนาดเล็กใน Java

package com.example.app.callbacks;
public abstract class Callback1<T> {
    public void invoke(T obj) {}
}

package com.example.app.callbacks;
public abstract class Callback2<T, T2> {
    public void invoke(T obj, T2 obj2) {}
}

package com.example.app.callbacks;
public abstract class Callback3<T, T2, T3> {
    public void invoke(T obj, T2 obj2, T3 obj3) {}
}

...ETC

วิธี Java ดูเหมือนว่า:

public void myMethod(String par1, String par2, final Callback1<int> callback, final Callback2<int, String> callback2) {
    //Async Code 
        callback.invoke(1);
        callback2.invoke(4, "str");
}

ตอนนี้เมื่อเรียกมันใน Java:

utils.myMethod("par1", "par2", new Callback<int>() {
    @Override
    public void invoke(int obj) {
        super.invoke(obj);
        //cb result
    }
}, new Callback2<int, String>() {
    @Override
    public void invoke(int obj, String obj2) {
        super.invoke(obj, obj2);
        //cb2 result
    }
});

นอกจากนี้ยังใช้งานได้โดยการส่ง / ตั้งค่าการเรียกกลับของคุณไปยังคลาสที่คุณต้องการเรียกใช้วิธีเดียวกันนี้สามารถใช้ในการสร้างอินเทอร์เฟซได้เช่นกัน:

package com.example.app.interfaces;
public interface MyInterface<T> {
    void makeDo(T obj);
    void makeAnotherDo();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.