Вложенные функции в 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);
}
}
Ответы
Ответ 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
для создания эффективной подпрограммы внутри метода. Это уродливое и опасное, но намного проще, чем то, что было показано в предыдущих ответах. Хотя частный метод с вызовом внутри первого метода намного лучше, и он вам очень нужен. Я не знаю, почему вы хотели бы использовать вложенный метод для чего-то столь же простого, как это.