Вложенные функции в Java - PullRequest
       20

Вложенные функции в Java

42 голосов
/ 10 сентября 2011

Существуют ли какие-либо расширения для языка программирования Java, которые позволяют создавать вложенные функции?Во многих ситуациях мне нужно создавать методы, которые используются только один раз в контексте другого метода или цикла for.До сих пор я не смог сделать это в Java, хотя это легко сделать в Javascript.

Например, это невозможно сделать в стандартном Java:

for(int i = 1; i < 100; i++){
    times(2); //multiply i by 2 and print i
    times(i); //square i and then print the result
    public void times(int num){

        i *= num;
        System.out.println(i);
    }    
}

Ответы [ 6 ]

42 голосов
/ 18 февраля 2015

Java 8 представляет лямбды.

java.util.function.BiConsumer<Integer, Integer> times = (i, num) -> {
    i *= num;
    System.out.println(i);
};
for (int i = 1; i < 100; i++) {
    times.accept(i, 2); //multiply i by 2 and print i
    times.accept(i, i); //square i and then print the result
}

Синтаксис () -> работает на любом интерфейсе, который определяет только один метод.Таким образом, вы можете использовать его с Runnable, но он не работает с List.

BiConsumer - это один из многих функциональных интерфейсов, предоставляемых java.util.function .

Стоит отметить, что под капотом это определяет анонимный класс и создает его экземпляр.times является ссылкой на экземпляр.

24 голосов
/ 10 сентября 2011

Ответ ниже говорит о том, что ближе всего вы можете получить к вложенным функциям в Java до Java 8. Это не обязательно способ, с помощью которого я бы выполнял те же задачи, которые могут быть обработаны с помощью вложенных функций в Javascript. Часто частный вспомогательный метод будет работать так же хорошо - возможно, даже частный вспомогательный тип , для которого вы создаете экземпляр внутри метода, но который доступен для всех методов.

Конечно, в Java 8 есть лямбда-выражения, которые являются гораздо более простым решением.


Самое близкое, к чему вы можете легко прийти - это анонимный внутренний класс. Это настолько близко, насколько Java подходит к замыканиям на данный момент, хотя, надеюсь, будет больше поддержки в Java 8.

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

Так (ужасно) извращать ваш пример:

interface Foo {
    void bar(int x);
}

public class Test {
    public static void main(String[] args) {
        // Hack to give us a mutable variable we can
        // change from the closure.
        final int[] mutableWrapper = { 0 };

        Foo times = new Foo() {
            @Override public void bar(int num) {
                mutableWrapper[0] *= num;
                System.out.println(mutableWrapper[0]);
            }
        };

        for (int i = 1; i < 100; i++) {
            mutableWrapper[0] = i;
            times.bar(2);
            i = mutableWrapper[0];

            times.bar(i);
            i = mutableWrapper[0];
        }
    }
}

Выход:

2
4
10
100

Это вывод, который вы получаете из Javascript?

22 голосов
/ 17 сентября 2013

Я думаю, что ближе всего к вложенным функциям в Java 7 вы можете использовать не анонимный внутренний класс (ответ Джона Скита выше), а использование очень редко используемых локальных классов.Таким образом, даже интерфейс вложенного класса не виден за пределами его предполагаемой области видимости, и он немного менее многословен.

Пример Джона Скита, реализованный с локальным классом, будет выглядеть следующим образом:

public class Test {
    public static void main(String[] args) {
        // Hack to give us a mutable variable we can
        // change from the closure.
        final int[] mutableWrapper = { 0 };

        class Foo {
            public void bar(int num) {
                mutableWrapper[0] *= num;
                System.out.println(mutableWrapper[0]);
            }
        };

        Foo times = new Foo();

        for (int i = 1; i < 100; i++) {
            mutableWrapper[0] = i;
            times.bar(2);
            i = mutableWrapper[0];

            times.bar(i);
            i = mutableWrapper[0];
        }
    }
}

Выход:

2
4
10
100
2 голосов
/ 10 сентября 2011

Такие методы иногда называют замыканиями. Взгляните на Groovy - возможно, вы предпочтете его Java. В Java 8, вероятно, также будут замыкания (см. JSR335 и отложенный список ).

0 голосов
/ 04 августа 2017

Рассмотрите возможность создания анонимного локального класса и использования его блока инициализатора для выполнения работы:

public class LocalFunctionExample {
    public static void main(final String[] args) {
        for (final int i[] = new int[] { 1 }; i[0] < 100; i[0]++) {
            new Object() {
                {
                    times(2); //multiply i by 2 and print i
                    times(i[0]); //square i and then print the result
                }

                public void times(final int num) {
                    i[0] *= num;
                    System.out.println(i[0]);
                }
            };
        }
    }
}

Выход:

2
4
10
100

(«Последний трюк с оберткой» автоматически не требуется при использовании этой техники, но здесь он необходим для обработки требования мутации.)

Это работает почти так же лаконично, как лямбда-версия, но вы можете использовать любые сигнатуры методов, которые вам нужны, они получают реальные имена параметров, а методы вызываются напрямую по их именам - не нужно .apply() или еще много чего. (Подобные вещи иногда делают работу инструментов IDE тоже немного лучше.)

0 голосов
/ 28 февраля 2013

Я не хочу использовать запрещенное слово, но вы можете использовать оператор goto, чтобы создать эффективную подпрограмму внутри метода.Это уродливо и опасно, но гораздо проще, чем было показано в предыдущих ответах.Хотя закрытый метод с вызовом внутри первого метода намного лучше, и обслуживает вас просто отлично.Я не знаю, почему вы хотите использовать вложенный метод для чего-то такого простого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...