Подтвердить что ты не робот

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

Существуют ли какие-либо расширения для языка программирования Java, которые позволяют создавать вложенные функции? Существует много ситуаций, когда мне нужно создавать методы, которые используются только один раз в контексте другого метода или for-loop. Я до сих пор не смог выполнить это в 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);
    }    
}
4b9b3361

Ответ 1

Java 8 вводит lambdas.

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 является ссылкой на экземпляр.

Ответ 2

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

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


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

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

Итак, чтобы (ужасно) извратить ваш пример:

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?

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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 немного лучше.)

Ответ 6

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