Почему рекурсивный вызов этой функции не вызывает исключение NullPointerException

Мой вопрос исходит из этой потока.

Рассмотрим этот код:

public class Test {    
    static Function<Integer, Integer> fibLambda = null;
    public static void main (String[] args) { 
        fibLambda = n -> n <= 2 ? 1 : fibLambda.apply(n - 1) + fibLambda.apply(n - 2); 
        System.out.println(fibLambda.apply(6));
    }
}

Результат выше равен 8.

Чего я не понимаю, так это того, как инициализируется fibLamdba? Кажется, я совершенно не понимаю, как выполняется вызов метода, потому что я думал, что этот код создаст NPE.

Надеюсь, мой вопрос понятен


person user2336315    schedule 04.01.2015    source источник
comment
Что говорит первая строка main? fibLambda = . Почему бы не установить...?   -  person cHao    schedule 04.01.2015
comment
Я не понимаю, почему это ДОЛЖНО вызывать NPE.   -  person MightyPork    schedule 04.01.2015
comment
@cHao Да, но там написано fibLamdba = .. fibLambda.someFunction(..);   -  person user2336315    schedule 04.01.2015
comment
Ох. Вы никогда раньше не видели закрытия, не так ли? Это немного похоже на это.   -  person cHao    schedule 04.01.2015
comment
@cHao Это действительно закрытие? Для меня замыкание — это функция, которая возвращает функцию, которая зависит от параметра, переданного включающую функцию.   -  person Dici    schedule 04.01.2015
comment
@Dici: Да, это не совсем закрытие. Но идея, которая заставляет это работать, очень похожа. Значение fibLambda — это значение, существующее во время вызова.   -  person cHao    schedule 04.01.2015


Ответы (1)


Ваш код эквивалентен

static Function<Integer, Integer> fibLambda = null;

public static void main(String[] args) {
    fibLambda = n -> n <= 2 ? 1 : Example.fibLambda.apply(n - 1) + Example.fibLambda.apply(n - 2);
    System.out.println(fibLambda.apply(6));
}

К тому времени, когда apply вызывается, fibLambda присваивается значение. По сути, лямбда-выражение не фиксирует значение fibLambda, оно просто регистрирует, что переменная должна быть оценена в соответствующий момент для получения значения.

Помните, что лямбда-выражение не выполняет код, находящийся в его теле. Это просто объявление, подобное тому, как вы объявляете экземпляр анонимного класса.

person Sotirios Delimanolis    schedule 04.01.2015
comment
По сути, лямбда-выражение не фиксирует значение fibLambda, оно просто регистрирует, что переменная должна быть оценена в соответствующий момент для получения значения. Спасибо, теперь я понимаю. Меня это беспокоило, теперь понятно :) - person user2336315; 04.01.2015
comment
@user2336315 user2336315 Просто для ясности, это потому, что fibLambda - это поле класса. Если бы это была локальная переменная, все было бы иначе. Он будет оценен сразу же, и его значение будет зафиксировано для использования в реализации. - person Sotirios Delimanolis; 04.01.2015
comment
@SotiriosDelimanolis после вашего замечания я попытался сделать его локальным и не нашел способа сделать то же самое. Это возможно ? - person Dici; 04.01.2015
comment
@Dici Не напрямую. Вот что пытается объяснить Jon Skeet answer. - person Sotirios Delimanolis; 04.01.2015