Làm cách nào để viết một hàm ẩn danh trong Java?


87

Nó thậm chí có thể?


6
Lưu ý rằng điều này hiện có thể thực hiện được trong Java 8 - hãy xem câu trả lời về Biểu thức Lambda của Mark Rotteveel bên dưới.
Josiah Yoder

Câu trả lời:


81

nếu bạn muốn nói đến một hàm ẩn danh và đang sử dụng phiên bản Java trước Java 8, thì trong một từ, không. ( Đọc về biểu thức lambda nếu bạn sử dụng Java 8+ )

Tuy nhiên, bạn có thể triển khai một giao diện có chức năng như sau:

Comparator<String> c = new Comparator<String>() {
    int compare(String s, String s2) { ... }
};

và bạn có thể sử dụng điều này với các lớp bên trong để có được một hàm gần như ẩn danh :)


6
Chưa. Trong Java 7, điều đó có thể xảy ra: stackoverflow.com/questions/233579/closures-in-java-7
Ilya Boyandin

2
Đồng thời, trong khi chờ đợi JDK7, các phương thức ẩn danh có thể được mô phỏng trong ngữ cảnh OO bằng cách sử dụng en.wikipedia.org/wiki/Command_pattern
gpampara

1
didnt closured làm cho nó thành Java 7.
Thorbjørn Ravn Andersen

5
Tôi nghĩ bạn nên thay đổi câu trả lời của bạn như chúng tôi có chức năng ẩn danh với Java 8.
Node.js

45

Đây là một ví dụ về một lớp bên trong ẩn danh.

System.out.println(new Object() {
    @Override public String toString() {
        return "Hello world!";
    }
}); // prints "Hello world!"

Điều này không hữu ích lắm nhưng nó chỉ ra cách tạo một thể hiện của một lớp bên trong ẩn danh extends Objectvà phương thức @Overridecủa nó toString().

Xem thêm


Các lớp ẩn danh bên trong rất tiện dụng khi bạn cần triển khai một lớp interfacecó thể không được tái sử dụng nhiều (và do đó không có giá trị cấu trúc lại thành lớp được đặt tên riêng của nó). Một ví dụ hướng dẫn đang sử dụng một tùy chỉnh java.util.Comparator<T>để sắp xếp.

Đây là một ví dụ về cách bạn có thể sắp xếp String[]dựa trên String.length().

import java.util.*;
//...

String[] arr = { "xxx", "cd", "ab", "z" };
Arrays.sort(arr, new Comparator<String>() {
    @Override public int compare(String s1, String s2) {
        return s1.length() - s2.length();
    }           
});
System.out.println(Arrays.toString(arr));
// prints "[z, cd, ab, xxx]"

Lưu ý thủ thuật so sánh từng phép trừ được sử dụng ở đây. Cần phải nói rằng kỹ thuật này bị hỏng nói chung: nó chỉ áp dụng khi bạn có thể đảm bảo rằng nó sẽ không bị tràn (chẳng hạn như trường hợp với Stringđộ dài).

Xem thêm


5
Phần lớn các lần xuất hiện khác có thể được tìm thấy dưới dạng EventListenertriển khai (phụ) trong ứng dụng Swing trung bình.
BalusC

@BalusC: đã thêm liên kết cho câu hỏi "chúng được sử dụng như thế nào"
polygenelubricants

@BalusC: stackoverflow gần đây đã thêm Linkedthanh bên, vì vậy tôi đang cố gắng hết sức để sử dụng nó.
polygenelubricants

12

Với sự ra đời của biểu thức lambda trong Java 8, giờ đây bạn có thể có các phương thức ẩn danh.

Giả sử tôi có một lớp Alphavà tôi muốn lọc Alphacác s theo một điều kiện cụ thể. Để làm điều này, bạn có thể sử dụng một Predicate<Alpha>. Đây là một giao diện chức năng có một phương thức testchấp nhận một Alphavà trả về a boolean.

Giả sử rằng phương thức lọc có chữ ký này:

List<Alpha> filter(Predicate<Alpha> filterPredicate)

Với giải pháp lớp ẩn danh cũ, bạn sẽ cần một cái gì đó như:

filter(new Predicate<Alpha>() {
   boolean test(Alpha alpha) {
      return alpha.centauri > 1;
   }
});

Với lambdas Java 8 bạn có thể làm:

filter(alpha -> alpha.centauri > 1);

Để biết thêm thông tin chi tiết, hãy xem hướng dẫn về Biểu thức Lambda


2
Tham khảo phương pháp cũng hữu ích. ví dụ: sắp xếp (String :: CompareToIgnoreCase
Josiah Yoder

9

Các lớp bên trong ẩn danh việc triển khai hoặc mở rộng giao diện của một kiểu hiện có đã được thực hiện trong các câu trả lời khác, mặc dù điều đáng chú ý là có thể triển khai nhiều phương thức (ví dụ: thường với các sự kiện kiểu JavaBean).

Một đặc điểm ít được công nhận là mặc dù các lớp ẩn danh bên trong không có tên, nhưng chúng có một kiểu. Các phương pháp mới có thể được thêm vào giao diện. Các phương thức này chỉ có thể được gọi trong một số trường hợp hạn chế. Chủ yếu trực tiếp trên newchính biểu thức và trong lớp (bao gồm cả các bộ khởi tạo cá thể). Nó có thể gây nhầm lẫn cho người mới bắt đầu, nhưng nó có thể "thú vị" đối với đệ quy.

private static String pretty(Node node) {
    return "Node: " + new Object() {
        String print(Node cur) {
            return cur.isTerminal() ?
                cur.name() :
                ("("+print(cur.left())+":"+print(cur.right())+")");
        }
    }.print(node);
}

(Ban đầu tôi đã viết điều này bằng cách sử dụng nodechứ không phải curtrong printphương pháp. Nói KHÔNG với việc bắt finalngười dân địa phương " ngầm "? )


nodenên được khai báo finalở đây.
BalusC

@BalusC Bắt đẹp. Thực ra sai lầm của tôi là không sử dụng cur.
Tom Hawtin - tackline

@Tom: +1 kỹ thuật hay! Nó có thực sự được sử dụng ở bất cứ đâu trong thực tế không? Bất kỳ tên cho mô hình cụ thể này?
polygenelubricants

@polygenelubricants Không xa như tôi biết. Chi phí thêm một đối tượng! (Và một lớp học.) Thành ngữ ngoặc kép cũng vậy. Những người có suy nghĩ đúng đắn dường như không bận tâm đến thành ngữ Execute Around.
Tom Hawtin - tackline

@polygenelubricants Trên thực tế, tôi dường như không có nhiều thuật toán đệ quy (độc lập). Đặc biệt là những phương thức không đệ quy đuôi (hoặc dễ dàng thực hiện như vậy) và không thể thực hiện bằng cách gọi phương thức public (lưu ý rằng hơi không liên quan "Node" +để thực hiện phương thức thứ hai). / Tôi không có tên. Có lẽ tôi có thể tạo ra một câu hỏi "thăm dò ý kiến" (CW) đặt tên, và đưa nó vào quên lãng.
Tom Hawtin - tackline

1

Có nếu bạn đang sử dụng java mới nhất là phiên bản 8. Java8 giúp bạn có thể xác định các hàm ẩn danh, điều không thể xảy ra trong các phiên bản trước.

Hãy lấy ví dụ từ tài liệu java để biết cách chúng ta có thể khai báo các hàm, lớp ẩn danh

Ví dụ sau, HelloWorldAnonymousClasses, sử dụng các lớp ẩn danh trong các câu lệnh khởi tạo của các biến cục bộ frenchGreeting và spanishGreeting, nhưng sử dụng một lớp cục bộ để khởi tạo biến englishGreeting:

public class HelloWorldAnonymousClasses {

    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }

    public void sayHello() {

        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }

        HelloWorld englishGreeting = new EnglishGreeting();

        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

        HelloWorld spanishGreeting = new HelloWorld() {
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }            
}

Cú pháp của các lớp ẩn danh

Xem xét việc tạo đối tượng frenchGreeting:

    HelloWorld frenchGreeting = new HelloWorld() {
        String name = "tout le monde";
        public void greet() {
            greetSomeone("tout le monde");
        }
        public void greetSomeone(String someone) {
            name = someone;
            System.out.println("Salut " + name);
        }
    };

Biểu thức lớp ẩn danh bao gồm những điều sau:

  • Người newđiều hành
  • Tên của một giao diện để triển khai hoặc một lớp để mở rộng. Trong ví dụ này, lớp ẩn danh đang triển khai giao diện HelloWorld.

  • Dấu ngoặc đơn chứa các đối số của một hàm tạo, giống như một biểu thức tạo cá thể lớp bình thường. Lưu ý: Khi bạn triển khai một giao diện, không có hàm tạo, vì vậy bạn sử dụng một cặp dấu ngoặc đơn trống, như trong ví dụ này.

  • Một phần thân, là phần tử khai báo lớp. Cụ thể hơn, trong phần thân, khai báo phương thức được phép nhưng câu lệnh thì không.

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.