Як я можу написати анонімну функцію на Java?


Відповіді:


81

якщо ви маєте на увазі анонімну функцію і використовуєте версію Java перед Java 8, то одним словом, ні. ( Прочитайте про лямбда-вирази, якщо ви використовуєте Java 8+ )

Однак ви можете реалізувати інтерфейс з такою функцією:

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

і ви можете використовувати це з внутрішніми класами, щоб отримати майже анонімну функцію :)


6
Ще ні. У Java 7 це стане можливим: stackoverflow.com/questions/233579/closures-in-java-7
Ілля Бояндін,

2
Тим часом, в очікуванні JDK7, анонімні методи можна емулювати в контексті ОО за допомогою en.wikipedia.org/wiki/Command_pattern
gpampara

1
closured не потрапив на Java 7.
Thorbjørn Ravn Andersen

5
Я думаю, вам слід змінити свою відповідь, оскільки ми маємо анонімну функцію з Java 8.
Node.JS

45

Ось приклад анонімного внутрішнього класу.

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

Це не дуже корисно, але воно показує, як створити екземпляр анонімного внутрішнього класу that extends Objectта @Overrideйого toString()метод.

Дивіться також


Анонімні внутрішні класи дуже зручні, коли вам потрібно реалізувати interfaceякий, можливо, не можна використовувати багаторазово (і, отже, не варто переробляти на власний іменований клас). Повчальним прикладом є використання спеціального java.util.Comparator<T>для сортування.

Ось приклад того, як можна сортувати на String[]основі 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]"

Зверніть увагу на трюк порівняння-віднімання, що використовується тут. Слід сказати, що ця техніка загалом порушена: вона застосовується лише тоді, коли ви можете гарантувати, що вона не переллється (такий випадок має Stringдовжина).

Дивіться також


5
Більшість інших випадків можна знайти як EventListener(під) реалізації в середньому додатку Swing.
BalusC,

@BalusC: додано посилання на запитання "як вони використовуються"
полігенмастильні

@BalusC: stackoverflow нещодавно додав Linkedбічну панель, тому я роблю все можливе, щоб нею скористатися.
полігенмастильні матеріали

12

З введенням лямбда-виразу в Java 8 тепер ви можете мати анонімні методи.

Скажімо, у мене є клас, Alphaі я хочу відфільтрувати Alphas за певною умовою. Для цього ви можете використовувати a Predicate<Alpha>. Це функціональний інтерфейс, який має метод, testякий приймає Alphaі повертає a boolean.

Припускаючи, що метод фільтра має такий підпис:

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

За допомогою старого рішення анонімного класу вам знадобиться щось на зразок:

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

З лямбдами Java 8 ви можете:

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

Для отримання більш детальної інформації див. Посібник Lambda Expressions


2
Посилання на методи також корисні. наприклад сортувати (String :: compareToIgnoreCase) docs.oracle.com/javase/tutorial/java/javaOO/…
Йодер

9

Анонімні внутрішні класи, що реалізують або розширюють інтерфейс існуючого типу, були зроблені в інших відповідях, хоча варто зауважити, що можуть бути реалізовані кілька методів (часто, наприклад, з подіями в стилі JavaBean).

Трохи визнаною особливістю є те, що, хоча анонімні внутрішні класи не мають імені, вони мають тип. До інтерфейсу можна додати нові методи. До цих методів можна звертатися лише в обмежених випадках. Головним чином безпосередньо на самому newвиразі та всередині класу (включаючи ініціалізатори екземплярів). Це може заплутати початківців, але це може бути "цікаво" для рекурсії.

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

(Я спочатку написав це , використовуючи nodeзамість того , curв printметоді. Скажи НІ перехоплюючі «неявно final» місцеві жителі? )


nodeслід оголосити finalтут.
BalusC

@BalusC Хороший улов. Насправді моєю помилкою було не використовувати cur.
Том Хоутін - таклін

@Tom: +1 приємна техніка! Чи насправді це десь використовується на практиці? Будь-яка назва цього конкретного зразка?
полігенмастильні матеріали

@polygenelubricants Не наскільки я знаю. Варто цілий додатковий об’єкт! (І клас.) Приблизно те саме стосувалося ідіоми подвійних дужок. Правильно мислячі люди, здається, не заперечують ідіому "Виконання навколо".
Том Хоутін - таклін

@polygenelubricants Насправді мені здається не так багато (автономних) рекурсивних алгоритмів. Особливо ті, які не є рекурсивними хвостами (або їх легко зробити) і не можуть бути реалізовані за допомогою виклику загальнодоступного методу (зверніть увагу на дещо неактуальне, "Node" +щоб зробити другий метод необхідним). / У мене немає імені. Можливо, я міг би створити запитання про "опитування" (CW), і проголосувати його забуваючи.
Том Хоутін - таклін

1

Так, якщо ви використовуєте останню java, яка є версією 8. Java8 надає можливість визначати анонімні функції, що було неможливо в попередніх версіях.

Давайте візьмемо приклад з java docs, щоб дізнатись, як ми можемо оголосити анонімні функції, класи

Наступний приклад, HelloWorldAnonymousClasses, використовує анонімні класи в операторах ініціалізації локальних змінних frenchGreeting і spanishGreeting, але використовує локальний клас для ініціалізації змінної 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();
    }            
}

Синтаксис анонімних класів

Розглянемо інстанціювання об’єкта 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);
        }
    };

Вираз анонімного класу складається з наступного:

  • newоператор
  • Ім'я інтерфейсу для реалізації або класу для розширення. У цьому прикладі анонімний клас реалізує інтерфейс HelloWorld.

  • Дужки, які містять аргументи конструктора, як і звичайний вираз створення екземпляра класу. Примітка: Коли ви реалізуєте інтерфейс, конструктор відсутній, тому ви використовуєте порожню пару дужок, як у цьому прикладі.

  • Тіло, яке є тілом оголошення класу. Більш конкретно, в тілі декларації методів дозволені, а оператори - ні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.