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

Поддерживает ли поддержка Java 8?

Я в замешательстве. Я думал, что Java8 выйдет из каменного века и начнет поддерживать лямбды/затворы. Но когда я пытаюсь:

public static void main(String[] args) {
    int number = 5;

    ObjectCallback callback = () -> {
        return (number = number + 1);
    };

    Object result = callback.Callback();
    System.out.println(result);
}

говорится, что number should be effectively final. Я думаю, что это не закрытие. Это звучит просто как копирование среды по значению, а не по ссылке.

Бонусный вопрос!

Поддерживает ли Android-поддержку функции Java-8?

4b9b3361

Ответ 1

Почему, почему, Java. Почему, почему?

Вам нужно провести длинную (приватную) дискуссию с соответствующими членами команды Oracle Java для получения истинного ответа. (Если они захотят поговорить с вами...)


Но я подозреваю, что это комбинация обратной совместимости и ограничений ресурсов проекта. И тот факт, что нынешний подход "достаточно хорош" с прагматической точки зрения.

Реализация контекстов процедуры как объектов первого класса (т.е. замыкания) требует, чтобы время жизни некоторых локальных переменных выходило за пределы возврата вызова метода объявления. Это означает, что вы не можете просто положить их в стек. Вместо этого вы оказываетесь в ситуации, когда некоторые локальные переменные должны быть полями объекта кучи. Это означает, что вам нужен новый вид скрытого класса ИЛИ фундаментальные изменения в архитектуре JVM.

Хотя технически это возможно реализовать, язык Java не является языком "зеленого поля". Изменение природы должно поддерживать "настоящие закрытия", было бы трудно:

  • Для Oracle и сторонних разработчиков потребуется огромное количество усилий, чтобы обновить все цепочки инструментов. (И мы говорим не только о компиляторах. Существуют отладчики, профилировщики, обфускаторы, байт-коды, инфраструктура персистентности...)

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

  • Существует потенциальное влияние на другие языки и т.д., которые каким-то образом используют JVM. Например, Android зависит от архитектуры JVM/файлов байт-кода как "языка ввода" для инструментальной цепочки Davlik. Существуют языковые реализации для Python, Ruby и различных функциональных языков, которые генерируют код для платформы JVM.


Короче говоря, "реальные закрытия" в Java были бы большим страшным предложением для всех заинтересованных сторон. "Блокировка для финальных" взломов - это прагматичный компромисс, который действительно работает, и на практике это достаточно хорошо.

Наконец, всегда существует вероятность того, что ограничение final может быть удалено в будущей редакции. (Я бы не задержал дыхание, хотя....)


Поддерживает ли Android-поддержку функции Java-8?

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

Но хорошей новостью является то, что расширения синтаксиса Java 7 теперь поддерживаются KitKat и соответствующими версиями Android Studio или Eclipse ADT.

Ответ 2

Вам нужно будет указать свое определение "закрытие".

Для меня "закрытие" - это что-то (функция или объект или другая вещь, которая может быть запущена каким-то образом подобно методам), которая фиксирует ( "закрывает" ) локальную переменную из ее охватывающей области и может используйте эту переменную в своем коде, даже если метод функции или объекта запущен позднее, в том числе, когда область приложения больше не существует. На разных языках может быть возможно захватить переменную по значению или по ссылке, или и то, и другое.

По этому определению Java-анонимные классы (которые были там с Java 1.1) являются закрытиями, поскольку они могут ссылаться на локальные переменные из своей охватывающей области.

Lambdas в Java 8 - это в основном частный случай анонимных классов (а именно анонимный класс, который реализует интерфейс с помощью одного метода ( "функциональный интерфейс" ) и который не имеет переменных экземпляра и который не относится к (используя this явно или неявно)). Любая лямбда может быть переписана в эквивалентное выражение анонимного класса. Итак, сказанное выше также относится к лямбдам.

Это, а не закрытие, я думаю.

Хорошо, вы, сэр, ошиблись в определении "закрытия".

Ответ 3

Я думаю, что ограничение final имеет технические причины. Выражение лямбда просто берет значение из контекста окружающего метода, потому что эта ссылка живет в стеке и не выживет после завершения метода.

Если вы поместите значение контекста в ссылку, вы можете построить "реальное" закрытие:

import java.util.function.Supplier;

public class CreatingAClosure {

    public static void main(String[] args) {
        Supplier<Supplier<String>> mutterfunktion = () -> {
            int container[] = {0};
            return () -> {
                container[0]++;
                return "Ich esse " + container[0] + " Kuchen.";
            };
        };
        Supplier<String> essen = mutterfunktion.get();
        System.out.println(essen.get());
        System.out.println(essen.get());
        System.out.println(essen.get());
    }
}

Ausgabe:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 3 Kuchen.

Вместо массива вы можете взять любой подходящий экземпляр любого объекта, потому что он живет в куче, и только ссылка на этот экземпляр сохраняется (окончательно) в выражении лямбда.

В этом случае значение container заключено в mutterfunktion. Каждый вызов mutterfunktion создает новый ссылочный экземпляр.

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

Вы также можете определить container в контексте метода, вы сможете делать изменения вне лямбда:

public static void main(String[] args) {
    int container[] = {0};
    Supplier<String> essen = () -> {
        container[0]++;
        return "Ich esse " + container[0] + " Kuchen.";
    };
    System.out.println(essen.get());
    System.out.println(essen.get());
    container[0]++;
    System.out.println(essen.get());
}

Ausgabe:

Ich esse 1 Kuchen.
Ich esse 2 Kuchen.
Ich esse 4 Kuchen.

Таким образом, ответ на ваш вопрос будет "да".

Ответ 4

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

@Test
public void clojureStateSnapshotTest() {
    Function wrapperFunc;
    wrapperFunc = (a) -> {
        // final reference
        final WrapLong outerScopeState = new WrapLong();

        outerScopeState.aLong = System.currentTimeMillis();
        System.out.println("outer scope state BEFORE: " + outerScopeState.aLong);

        Function closure = (b) -> {
            System.out.println("closure: " + outerScopeState.aLong);
            return b;
        };

        outerScopeState.aLong = System.currentTimeMillis();
        System.out.println("outer scope state AFTER: " + outerScopeState.aLong);

        // show correct snapshot state
        closure.apply(new Object());

        return a;
    };
    // init clojure
    wrapperFunc.apply(new Object());
}

public class WrapLong {
    public long aLong = 0;
}

но все же весело...