Tương đương của Java về Hàm và Hành động


Câu trả lời:


96

Trong Java 8, tương đương là giao diện java.util.function.Function<T, R>java.util.function.Consumer<T>giao diện. Tương tự, java.util.function.Predicate<T>là tương đương với System.Predicate<T>. Như đã đề cập ở những nơi khác, đây là các giao diện thay vì các đại biểu.

Liên quan sang một bên: Tôi hiện đang dựa rất nhiều vào lớp tiện ích sau để thực hiện nội dung phương thức mở rộng giống 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;
  }
}

Các phương thức giống như System.Linq.Enumerable.Where<TSource>System.Linq.Enumerable.Select<TSource, TResult>LINQ mà tôi trình bày ở đây không lười biếng và duyệt hoàn toàn các tập hợp nguồn trước khi trả về các tập hợp kết quả cho trình gọi. Tuy nhiên, tôi thấy chúng hữu ích cho các mục đích hoàn toàn về mặt cú pháp và có thể bị lười biếng nếu cần thiết. Được

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

Người ta có thể làm như sau:

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

Tôi thích cái nào sau đây:

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

1
Chúng tôi thực sự cần bỏ phiếu cho câu trả lời này vì câu hỏi này là kết quả tìm kiếm số 1 hiện tại cho "Java tương đương với hành động" và bây giờ là năm 2015 vì vậy nội dung của Java 8 tốt hơn nhiều so với những gì Java có trước đây và gần như bắt chước công cụ của .net ở đây điểm.
Robert C. Barth

1
Tôi nghĩ bạn có nghĩa là Iterable <Widget> filterWidgets = IterableUtil.where (widget, w -> w.name (). StartedWith ("một số tiền tố"));
electricalbah

1
Ngoài Function<T, R>Consumer<T>, bạn có thể tìm thấy toàn bộ các giao diện chức năng phổ biến mà Java cung cấp tại đây .
Jämes

35

Giao diện callable tương tự như Func.

Giao diện Runnable tương tự như Action.

Nói chung, Java sử dụng các lớp ẩn danh bên trong để thay thế cho các đại biểu C #. Ví dụ, đây là cách bạn thêm mã để phản ứng với thao tác nhấn nút trong GUI:

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

13
Điều phân biệt Func với Callable là có các quá tải chung cho tối đa 16 đối số (Func <TResult>, Func <T, TResult>, Func <T1, T2, TResult>, v.v.). OTOH, Callable không có đối số. Hơn nữa, không thể thực hiện quá tải của C # vì tính năng xóa kiểu trong generic.
Aleksandr Dubinsky

6

Sự sang trọng của các đại biểu Func quá tải (ngoài các đại biểu vs vấn đề giai cấp vô danh) là họ hỗ trợ 0-16 đối số ( Func<TResult>, Func<T, TResult>, Func<T1, T2, TResult>, vv)

Thật không may, điều này là không thể trong Java vì tính năng xóa kiểu. Các lớp không thể khác nhau bởi các tham số kiểu chung.

Java 8 bây giờ mang đến một vườn thú các tên như BiConsumerfor Action<T, T2>and, bởi vì Java không cho phép các đối số kiểu nguyên thủy BiIntConsumer,. Tuy nhiên, "sở thú" không lớn lắm và tôi không biết có thư viện nào mở rộng nó. Có một đề xuất tuyệt vời cho các chữ kiểu hàm như thế (int, int) => voidnhưng nó đã không được thông qua.


3
Điều thú vị là ở các lớp cấp CLR chỉ khác nhau về số lượng tham số chung lại có tên khác nhau. Func`1vv Nó chỉ là C # ánh xạ những cái đó với cùng một tên.
CodesInChaos

@CodesInChaos Ahh, rất thú vị. Thật tệ là Java cũng không làm theo cách này. Btw, Java 8 bây giờ mang đến một vườn thú các tên như BiConsumerfor Action<T, T2>and, bởi vì Java không cho phép các tham số kiểu nguyên thủy BiIntConsumer,. Có một đề xuất cho các chữ kiểu hàm như (int, int) => voidnhưng nó không được chấp nhận.
Aleksandr Dubinsky

5

Để Func<T>sử dụng: java.util. Chức năng.Supplier http://docs.oracle.com/javase/8/docs/api/java/util/ Chức năng/ Supplier.html


Suppliersẽ tương đương với Func<T>(trái ngược với Func<T1, T2>) không phải Action. An Actionkhông chấp nhận đối số và không trả về kết quả. (Các phiên bản khác của Actionchấp nhận nhiều đối số khác nhau và không trả về kết quả.)
Servy

Vâng bạn đúng, sai lầm của tôi. Tôi chạy vào bài đăng này vì tôi đang tìm kiếm Func<T>Java và nhớ nhầm nó là Action<T>. Rất tiếc
Tim Schruben

Câu trả lời vẫn hữu ích cho tôi. Có phải java cũng có một cái gì đó như Action<>: 0 đầu vào, 0 đầu ra. Tốt nhất với .andThen(...)chức năng.
Kolya Ivankov

Tôi không biết về bất kỳ thứ gì được cung cấp bởi khung công tác Java giống như Action<>với 0 đầu vào và 0 đầu ra. Nhưng hãy nhớ rằng, trong Java đây chỉ là những giao diện. Vì vậy, bạn có thể tạo của riêng bạn để sử dụng.
Tim Schruben

Runnablecho Action<>, mặc dù nó không hoàn toàn đẹp để sử dụng như công cụ chức năng Java 8 mới.
Mark K Cowan

3

Thực sự không có tương đương cho những thứ đó. Bạn có thể tạo các lớp bên trong ẩn danh trong Java, nhưng có xu hướng có các giao diện cụ thể hơn là các giao diện chung chung như Func và Action.


3

Java không có khái niệm về đại biểu. Để biết phương pháp giải quyết vấn đề, vui lòng xem Một lập trình viên Java đang xem xét các đại biểu C # :

Trong khi C # có một tập hợp các khả năng tương tự như Java, nó đã được bổ sung một số tính năng mới và thú vị. Ủy quyền là khả năng coi một phương thức như một đối tượng hạng nhất. AC # ủy quyền được sử dụng khi các nhà phát triển Java sẽ sử dụng một giao diện với một phương thức duy nhất. Trong bài viết này, việc sử dụng các đại biểu trong C # được thảo luận và mã được trình bày cho một đối tượng Đại biểu Java có thể thực hiện một chức năng tương tự. Tải xuống mã nguồn tại đây.


3

Bạn có thể sử dụng java.util.Function như thế này

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

Nhưng nếu bạn sử dụng nó với nhiều hơn một đối số (như C # Func là như vậy), thì bạn phải xác định phiên bản FunctionInterface của mình như sau

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

Sau đó, bạn có thể sử dụng với biến không có đối số

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

Đối với các phiên bản cũ hơn Java 8

Đối với các lệnh gọi lại phương thức trong C # mà tôi đã sử dụng như thế này:

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

và gọi nó là:

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

Tôi đã tạo các lớp trừu tượng nhỏ trong 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

Phương pháp Java trông giống như:

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

Bây giờ khi gọi nó bằng 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
    }
});

Điều này cũng hoạt động bằng cách chuyển / đặt lệnh gọi lại của bạn đến các lớp mà bạn muốn gọi chúng, phương pháp tương tự cũng có thể được sử dụng để tạo Giao diện:

package com.example.app.interfaces;
public interface MyInterface<T> {
    void makeDo(T obj);
    void makeAnotherDo();
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.