Cái gì thay thế gần nhất cho một con trỏ hàm trong Java?


299

Tôi có một phương pháp khoảng mười dòng mã. Tôi muốn tạo ra nhiều phương thức thực hiện chính xác cùng một thứ, ngoại trừ một phép tính nhỏ sẽ thay đổi một dòng mã. Đây là một ứng dụng hoàn hảo để truyền vào một con trỏ hàm để thay thế một dòng đó, nhưng Java không có các con trỏ hàm. Thay thế tốt nhất của tôi là gì?


5
Java 8 sẽ có biểu thức Lambda . Bạn có thể đọc thêm về biểu thức lambda ở đây .
Marius

4
@Marius Tôi không nghĩ các biểu thức lambda được tính là con trỏ hàm. Nhà ::điều hành, mặt khác ...
The Guy with The Hat

Xin lỗi vì nhận xét muộn;) - thông thường, bạn không cần một con trỏ hàm cho điều đó. Chỉ cần sử dụng một phương pháp mẫu! ( en.wikipedia.org/wiki/Template_method_potype )
isnot2bad

2
@ isnot2bad - nhìn vào bài báo đó, có vẻ như quá mức cần thiết - phức tạp hơn câu trả lời được đưa ra ở đây. Cụ thể, phương thức mẫu yêu cầu tạo một lớp con cho mỗi phép tính thay thế. Tôi không thấy OP đã nêu bất cứ điều gì yêu cầu các lớp con ; anh ta chỉ đơn giản muốn tạo ra một số phương thức và chia sẻ hầu hết việc thực hiện. Như câu trả lời được chấp nhận cho thấy, điều này dễ dàng được thực hiện "tại chỗ" (bên trong mỗi phương thức), ngay cả trước Java 8 với lambdas của nó.
ToolmakerSteve

@ToolmakerSteve Giải pháp được chấp nhận cũng yêu cầu lớp trên mỗi phép tính (ngay cả khi nó chỉ là lớp bên trong ẩn danh). Và mẫu phương thức mẫu cũng có thể được nhận ra bằng cách sử dụng các lớp bên trong ẩn danh, do đó nó không khác nhiều so với giải pháp được chấp nhận liên quan đến chi phí chung (trước Java 8). Vì vậy, đây là câu hỏi về mô hình sử dụng và các yêu cầu chi tiết mà chúng ta không biết. Tôi đánh giá cao câu trả lời được chấp nhận và chỉ muốn thêm một khả năng khác để suy nghĩ.
isnot2bad

Câu trả lời:


269

Lớp bên trong vô danh

Giả sử bạn muốn có một hàm được truyền vào với một Stringparam trả về một int.
Trước tiên, bạn phải xác định một giao diện với chức năng là thành viên duy nhất của nó, nếu bạn không thể sử dụng lại giao diện hiện có.

interface StringFunction {
    int func(String param);
}

Một phương thức lấy con trỏ sẽ chỉ chấp nhận StringFunctionthể hiện như vậy:

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

Và sẽ được gọi như vậy:

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

EDIT: Trong Java 8, bạn có thể gọi nó bằng biểu thức lambda:

ref.takingMethod(param -> bodyExpression);

13
Nhân tiện, đây là một ví dụ về "Người cha chỉ huy". vi.wikipedia.org/wiki/Command_Potype
Ogre Psalm33

3
@Ogre Psalm33 Kỹ thuật này cũng có thể là Mẫu chiến lược, tùy thuộc vào cách bạn sử dụng nó. Sự khác biệt giữa Mẫu chiến lược và Mẫu lệnh .
Rory O'Kane

2
Đây là một triển khai đóng cho Java 5, 6 và 7 mseifed.blogspot.se/2012/09/NH Nó chứa tất cả mọi người có thể yêu cầu ... Tôi nghĩ nó khá tuyệt vời!
mmm

@SecretService: Liên kết đó đã chết.
Lawrence Dol

@LawrenceDol Vâng, đúng vậy. Đây là một pastebin của lớp mà tôi đang sử dụng. pastebin.com/b1j3q2Lp
mmm

32

Đối với mỗi "con trỏ hàm", tôi sẽ tạo một lớp functor nhỏ thực hiện phép tính của bạn. Xác định một giao diện mà tất cả các lớp sẽ thực hiện và chuyển các thể hiện của các đối tượng đó vào hàm lớn hơn của bạn. Đây là sự kết hợp giữa " mẫu lệnh " và " mẫu chiến lược ".

@ ví dụ của sblund là tốt.


28

Khi có một số lượng tính toán khác nhau được xác định trước mà bạn có thể thực hiện trong một dòng đó, sử dụng một enum là một cách nhanh chóng nhưng rõ ràng để thực hiện một mẫu chiến lược.

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

Rõ ràng, khai báo phương thức chiến lược, cũng như chính xác một thể hiện của mỗi triển khai đều được định nghĩa trong một lớp / tệp duy nhất.


24

Bạn cần tạo một giao diện cung cấp (các) chức năng mà bạn muốn vượt qua. ví dụ:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

Sau đó, khi bạn cần truyền một hàm, bạn có thể thực hiện giao diện đó:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

Cuối cùng, hàm map sử dụng hàm được truyền trong Function1 như sau:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

Bạn có thể thường xuyên sử dụng Runnable thay vì giao diện của riêng mình nếu bạn không cần truyền tham số hoặc bạn có thể sử dụng nhiều kỹ thuật khác để làm cho thông số ít bị "cố định" hơn nhưng thường là đánh đổi với an toàn kiểu. (Hoặc bạn có thể ghi đè hàm tạo cho đối tượng hàm của bạn để vượt qua các tham số theo cách đó .. có rất nhiều cách tiếp cận và một số hoạt động tốt hơn trong một số trường hợp nhất định.)


4
Câu trả lời này của người Viking liên quan nhiều đến vấn đề được đặt ra hơn là tập hợp giải pháp.
tchrist

18

Tham chiếu phương thức bằng ::toán tử

Bạn có thể sử dụng các tham chiếu phương thức trong các đối số phương thức trong đó phương thức chấp nhận giao diện chức năng . Giao diện chức năng là bất kỳ giao diện nào chỉ chứa một phương thức trừu tượng. (Giao diện chức năng có thể chứa một hoặc nhiều phương thức mặc định hoặc phương thức tĩnh.)

IntBinaryOperatorlà một giao diện chức năng. Phương thức trừu tượng của nó applyAsInt, chấp nhận hai ints làm tham số của nó và trả về một int. Math.maxcũng chấp nhận hai ints và trả về một int. Trong ví dụ này, A.method(Math::max);làm cho parameter.applyAsIntgửi hai giá trị đầu vào của nó đến Math.maxvà trả lại kết quả của việc đó Math.max.

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

Nói chung, bạn có thể sử dụng:

method1(Class1::method2);

thay vì:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

viết tắt của:

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});

Để biết thêm thông tin, hãy xem toán tử :: (dấu hai chấm) trong Java 8Đặc tả ngôn ngữ Java §15.13 .


15

Bạn cũng có thể làm điều này (trong một số dịp RARE có ý nghĩa). Vấn đề (và nó là một vấn đề lớn) là bạn mất tất cả các loại an toàn khi sử dụng một lớp / giao diện và bạn phải đối phó với trường hợp phương thức không tồn tại.

Nó có "lợi ích" là bạn có thể bỏ qua các hạn chế truy cập và gọi các phương thức riêng tư (không được hiển thị trong ví dụ, nhưng bạn có thể gọi các phương thức mà trình biên dịch thường không cho phép bạn gọi).

Một lần nữa, đây là một trường hợp hiếm hoi mà điều này có ý nghĩa, nhưng trong những dịp đó, nó là một công cụ tốt để có.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}

1
Đôi khi tôi sử dụng điều này để xử lý menu / GUI vì cú pháp phương thức đơn giản hơn nhiều so với cú pháp lớp bên trong ẩn danh. Thật gọn gàng, nhưng bạn đang thêm vào sự phức tạp của sự phản chiếu mà một số người không muốn đào sâu vào, vì vậy hãy chắc chắn rằng bạn hiểu đúng và có các lỗi văn bản rõ ràng cho mọi tình trạng lỗi có thể xảy ra.
Bill K

Bạn có thể thực hiện gõ một cách an toàn bằng cách sử dụng thuốc generic và bạn không cần phản ánh.
Luigi Plinge

1
Tôi không thấy cách sử dụng generic và không sử dụng sự phản chiếu sẽ cho phép bạn gọi một phương thức bằng một tên có trong Chuỗi?
TofuBeer

1
@LuigiPlinge - bạn có thể cung cấp một đoạn mã về ý của bạn không?
ToolmakerSteve

13

Nếu bạn chỉ có một dòng khác nhau, bạn có thể thêm một tham số như cờ và câu lệnh if (cờ) gọi một dòng này hoặc dòng kia.


1
Câu trả lời của javaslook có vẻ như là một cách sạch hơn để làm điều này, nếu có nhiều hơn hai biến thể tính toán. Hoặc nếu một người muốn nhúng mã vào phương thức, thì một enum cho các trường hợp khác nhau mà phương thức xử lý và một công tắc.
ToolmakerSteve

1
@ToolmakerSteve đúng, mặc dù hôm nay bạn sẽ sử dụng lambdas trong Java 8.
Peter Lawrey


11

Các giao diện chức năng và các tham chiếu phương thức Java 8 mới bằng cách sử dụng ::toán tử.

Java 8 có thể duy trì các tham chiếu phương thức (MyClass :: new) với các con trỏ " @ Functional Interface ". Không cần tên phương thức giống nhau, chỉ cần chữ ký phương thức giống nhau.

Thí dụ:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

Vì vậy, những gì chúng ta có ở đây?

  1. Giao diện chức năng - đây là giao diện, có chú thích hoặc không có @FeftalInterface , chỉ chứa một khai báo phương thức.
  2. Tham chiếu phương thức - đây chỉ là cú pháp đặc biệt, trông như thế này, objectInstance :: methodName , không hơn không kém.
  3. Ví dụ sử dụng - chỉ là một toán tử gán và sau đó gọi phương thức giao diện.

BẠN NÊN SỬ DỤNG TƯƠNG TÁC CHỨC NĂNG CHỈ DÀNH CHO NGƯỜI NGHE DUY NHẤT VÀ CHỈ DÀNH CHO ĐÓ!

Bởi vì tất cả các con trỏ hàm như vậy thực sự không tốt cho khả năng đọc mã và khả năng hiểu. Tuy nhiên, tham chiếu phương thức trực tiếp đôi khi có ích, ví dụ với foreach.

Có một số Giao diện chức năng được xác định trước:

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

Đối với các phiên bản Java trước đó, bạn nên dùng thử Thư viện Guava, có chức năng và cú pháp tương tự, như Adrian Petrescu đã đề cập ở trên.

Để có nghiên cứu bổ sung, hãy xem Java 8 Chcoateet

và cảm ơn The Guy with The Hat cho đặc tả ngôn ngữ Java §15.13 .


7
" Bởi vì tất cả những thứ khác ... thực sự tệ cho khả năng đọc mã " là một tuyên bố hoàn toàn vô căn cứ, và sai, bên cạnh đó.
Lawrence Dol

9

Câu trả lời của @ sblundy là tuyệt vời, nhưng các lớp bên trong ẩn danh có hai lỗ hổng nhỏ, vấn đề chính là chúng có xu hướng không thể tái sử dụng và thứ cấp là một cú pháp cồng kềnh.

Điều tuyệt vời là mô hình của anh ta mở rộng thành các lớp đầy đủ mà không có bất kỳ thay đổi nào trong lớp chính (lớp thực hiện các phép tính).

Khi bạn khởi tạo một lớp mới, bạn có thể truyền tham số vào lớp đó có thể đóng vai trò là hằng số trong phương trình của bạn - vì vậy nếu một trong các lớp bên trong của bạn trông như thế này:

f(x,y)=x*y

nhưng đôi khi bạn cần một thứ đó là:

f(x,y)=x*y*2

và có thể một phần ba đó là:

f(x,y)=x*y/2

thay vì tạo hai lớp bên trong ẩn danh hoặc thêm tham số "thông qua", bạn có thể tạo một lớp THỰC TẾ duy nhất mà bạn khởi tạo là:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

Nó chỉ đơn giản là lưu trữ hằng số trong lớp và sử dụng nó trong phương thức được chỉ định trong giao diện.

Thực tế, nếu BIẾT rằng chức năng của bạn sẽ không được lưu trữ / tái sử dụng, bạn có thể làm điều này:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

Nhưng các lớp bất biến sẽ an toàn hơn - tôi không thể đưa ra lời biện minh để tạo ra một lớp như thế này.

Tôi thực sự chỉ đăng bài này vì tôi co rúm mỗi khi nghe thấy lớp bên trong ẩn danh - Tôi đã thấy rất nhiều mã dự phòng là "Bắt buộc" bởi vì điều đầu tiên mà lập trình viên đã làm là ẩn danh khi anh ta nên sử dụng một lớp thực tế và không bao giờ suy nghĩ lại quyết định của mình.


Huh? OP đang nói về các tính toán khác nhau (thuật toán; logic); bạn đang hiển thị các giá trị khác nhau (dữ liệu). Bạn cho thấy một trường hợp cụ thể trong đó sự khác biệt có thể được kết hợp thành một giá trị, nhưng đó là một sự đơn giản hóa vô lý của vấn đề đang được đặt ra.
ToolmakerSteve

6

Các thư viện Google Guava , đang trở nên rất phổ biến, có một đối tượng Chức năngDự đoán chung mà họ đã làm việc trong nhiều phần của API.


Câu trả lời này sẽ hữu ích hơn nếu nó cung cấp chi tiết mã. Lấy mã được hiển thị trong câu trả lời được chấp nhận và hiển thị giao diện của nó bằng Hàm.
ToolmakerSteve

4

Âm thanh như một mô hình chiến lược với tôi. Kiểm tra các mẫu Java fluffycat.com.


4

Một trong những điều tôi thực sự nhớ khi lập trình trong Java là gọi lại hàm. Một tình huống mà sự cần thiết phải tiếp tục trình bày là trong việc phân cấp đệ quy trong đó bạn muốn thực hiện một số hành động cụ thể cho từng mục. Giống như đi trên cây thư mục hoặc xử lý cấu trúc dữ liệu. Sự tối giản trong tôi ghét phải xác định một giao diện và sau đó là một triển khai cho từng trường hợp cụ thể.

Một ngày tôi thấy mình tự hỏi tại sao không? Chúng ta có con trỏ phương thức - đối tượng Phương thức. Với việc tối ưu hóa trình biên dịch JIT, việc gọi phản xạ thực sự không còn phải chịu một hình phạt hiệu năng lớn nữa. Và bên cạnh việc sao chép một tập tin từ vị trí này sang vị trí khác, chi phí của lời gọi phương thức được phản ánh sẽ trở nên không đáng kể.

Khi tôi nghĩ nhiều hơn về nó, tôi nhận ra rằng một cuộc gọi lại trong mô hình OOP yêu cầu ràng buộc một đối tượng và một phương thức với nhau - nhập đối tượng Gọi lại.

Kiểm tra giải pháp dựa trên phản ánh của tôi cho Callbacks trong Java . Miễn phí cho mọi mục đích sử dụng.


4

oK, chủ đề này đã đủ cũ, vì vậy rất có thể câu trả lời của tôi không hữu ích cho câu hỏi. Nhưng vì chủ đề này đã giúp tôi tìm ra giải pháp của mình, dù sao tôi cũng sẽ đưa nó ra đây.

Tôi cần phải sử dụng một phương thức tĩnh biến đổi với đầu vào đã biết và đầu ra đã biết (cả hai ). Vì vậy, sau đó, biết gói và tên phương thức, tôi có thể làm việc như sau:

java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);

cho một hàm chấp nhận một nhân đôi làm tham số.

Vì vậy, trong tình huống cụ thể của tôi, tôi đã khởi tạo nó với

java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);

và gọi nó sau này trong một tình huống phức tạp hơn với

return (java.lang.Double)this.Function.invoke(null, args);

java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);

trong đó hoạt động là một giá trị kép tùy ý. Tôi đang nghĩ về việc có thể làm điều này trừu tượng hơn một chút và khái quát hóa nó, như SoftwareMonkey đã làm, nhưng hiện tại tôi đủ hạnh phúc với cách thức này. Ba dòng mã, không có lớp và giao diện cần thiết, điều đó không quá tệ.


cảm ơn Rob vì đã thêm phần đánh dấu code, tôi đã quá nôn nóng và ngu ngốc khi tìm thấy nó ;-)
Yogibimbi

4

Để làm điều tương tự mà không có giao diện cho một loạt các chức năng:

class NameFuncPair
{
    public String name;                // name each func
    void   f(String x) {}              // stub gets overridden
    public NameFuncPair(String myName) { this.name = myName; }
}

public class ArrayOfFunctions
{
    public static void main(String[] args)
    {
        final A a = new A();
        final B b = new B();

        NameFuncPair[] fArray = new NameFuncPair[]
        {
            new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
            new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
        };

        // Go through the whole func list and run the func named "B"
        for (NameFuncPair fInstance : fArray)
        {
            if (fInstance.name.equals("B"))
            {
                fInstance.f(fInstance.name + "(some args)");
            }
        }
    }
}

class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }

1
Tại sao? Điều này phức tạp hơn các giải pháp được đề xuất trước đây, chỉ cần một định nghĩa hàm ẩn danh cho mỗi phương án. Mỗi phương án, bạn tạo một lớp và một định nghĩa hàm ẩn danh. Tệ hơn, điều này được thực hiện ở hai vị trí khác nhau trong mã. Bạn có thể muốn cung cấp một số biện minh cho việc sử dụng phương pháp này.
ToolmakerSteve


3

Wow, tại sao không chỉ tạo một lớp Delegate mà không phải là tất cả những gì khó khăn mà tôi đã làm cho java và sử dụng nó để truyền tham số trong đó T là kiểu trả về. Tôi xin lỗi nhưng là một lập trình viên C ++ / C # nói chung chỉ học java, tôi cần các hàm con trỏ hàm vì chúng rất tiện dụng. Nếu bạn quen thuộc với bất kỳ lớp nào liên quan đến Thông tin phương pháp, bạn có thể làm điều đó. Trong các thư viện java đó sẽ là java.lang.reflect.method.

Nếu bạn luôn sử dụng một giao diện, bạn luôn phải thực hiện nó. Trong sự kiện, thực sự không có cách nào tốt hơn để đăng ký / hủy đăng ký khỏi danh sách các trình xử lý mà dành cho các đại biểu nơi bạn cần truyền vào các hàm chứ không phải loại giá trị, tạo một lớp đại biểu để xử lý nó cho các lớp ngoài giao diện.


Không phải là một câu trả lời hữu ích, trừ khi bạn hiển thị chi tiết mã. Làm thế nào để tạo một lớp Delegate giúp? Mã nào được yêu cầu cho mỗi thay thế?
ToolmakerSteve

2

Không có câu trả lời Java 8 nào đưa ra một ví dụ đầy đủ, gắn kết, vì vậy đây là câu trả lời.

Khai báo phương thức chấp nhận "con trỏ hàm" như sau:

void doCalculation(Function<Integer, String> calculation, int parameter) {
    final String result = calculation.apply(parameter);
}

Gọi nó bằng cách cung cấp hàm với biểu thức lambda:

doCalculation((i) -> i.toString(), 2);


1

Kể từ Java8, bạn có thể sử dụng lambdas, cũng có thư viện trong API SE 8 chính thức.

Cách sử dụng: Bạn cần sử dụng một giao diện chỉ với một phương thức trừu tượng. Tạo một ví dụ về nó (bạn có thể muốn sử dụng một java SE 8 đã được cung cấp) như thế này:

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

Để biết thêm thông tin, hãy kiểm tra tài liệu: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html


1

Trước Java 8, thay thế gần nhất cho chức năng giống như con trỏ hàm là một lớp ẩn danh. Ví dụ:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

Nhưng bây giờ trong Java 8, chúng ta có một biểu thức thay thế rất gọn gàng được gọi là biểu thức lambda , có thể được sử dụng như:

list.sort((a, b) ->  { a.isBiggerThan(b) } );

trong đó isBiggerThan là một phương thức trong CustomClass. Chúng tôi cũng có thể sử dụng tài liệu tham khảo phương pháp ở đây:

list.sort(MyClass::isBiggerThan);
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.