Представления Kotlin с синтетической привязкой и допуском значений NULL

Я заметил, что при использовании синтетической привязки Kotlin возвращаемое представление не равно нулю (Kotlin вернет View!). Но для меня это не имеет особого смысла, поскольку findCachedViewById может фактически возвращать нулевые результаты, что означает, что представления могут быть нулевыми.

public View _$_findCachedViewById(int var1) {
  if(this._$_findViewCache == null) {
     this._$_findViewCache = new HashMap();
  }

  View var2 = (View)this._$_findViewCache.get(Integer.valueOf(var1));
  if(var2 == null) {
     View var10000 = this.getView();
     if(var10000 == null) {
        return null;
     }

     var2 = var10000.findViewById(var1);
     this._$_findViewCache.put(Integer.valueOf(var1), var2);
  }

  return var2;
}

Так почему же они не являются обязательными в данном случае? Почему Kotlin просто не возвращает View? при использовании синтетической привязки, чтобы разработчики были вынуждены проверять допустимость значений NULL при работе с представлениями?

Может быть, это просто потому, что я новичок в Kotlin, но я думаю, что это немного интуитивно понятно, поскольку переменная не является необязательной, но мы все равно должны проверять, действительно ли View не является нулевым.

Итак, в этом случае имеет смысл делать что-то вроде приведенного ниже кода?

view?.let {
    // handle non null view here
}

person Guy    schedule 14.03.2018    source источник


Ответы (4)


Я понял это, я всегда нахожу правильный вопрос SO сразу после того, как отправляю свой :)

Единственный восклицательный знак после View на самом деле не означает, что представление не может быть нулевым, как я ожидал.

Этот ответ на другой вопрос по существу отвечает на мой точный вопрос. View при использовании синтетической привязки может фактически иметь значение NULL, но мы не можем знать наверняка, отсюда и единственный восклицательный знак.

Поэтому можно с уверенностью предположить, что код, который я опубликовал выше, - использование ?.let{...} - вполне приемлемый способ работы с представлениями, когда вы не уверены, инициализированы ли они уже при доступе к ним.

Случаи, когда представления могут быть нулевыми, очень редки, но это может случиться.

person Guy    schedule 14.03.2018
comment
Что же тогда с recyclerview? - person Dr. aNdRO; 07.02.2020

Как вы уже отметили, одиночный восклицательный знак не означает, что он не равен нулю, а скорее означает, что это тип платформы Java, и компилятор не знает, допускает ли он значение NULL или нет.

Я думаю, что то, что вы предложили, в порядке, хотя в фактическом случае нулевого значения, которое на самом деле может быть не тем, что вы хотите, молча терпит неудачу.

Допустим, вы пытались вызвать свое представление в onCreateView и забыли, что оно еще не инициализировано. Фрагмент не будет вести себя должным образом, но не вызовет значимой ошибки, которая поможет вам отладить проблему.

Я все еще пытаюсь выбрать то или иное решение, но я бы предложил либо явно обработать случай нулевого значения:

view?.let {
    //...
} ?: throwExceptionIfDebugElseLogToCrashlytics()

Или решите, что на этот раз вы действительно хотите, чтобы он генерировал исключение NullPointerException, и в этом случае я бы предложил:

view!!.let {
    //...
}

Последнее не раздувает ваш код из-за того, что «должно» быть невозможным крайним случаем, и не терпит неудач, но все же дает понять читателю, что представление может быть нулевым. Очевидно !! компилятору не требуется, он нужен только для того, чтобы сделать выбранную стратегию работы с типами платформ более явной.

person Mike Simpson    schedule 11.12.2018

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

Проблема в лямбде, и у Франтишека есть сообщение об этом здесь: https://stackoverflow.com/posts/comments/115183445?noredirect=1

person Milan Jurkulak    schedule 04.12.2020

Идея состоит в том, что макеты xml в Android довольно статичны, и для использования синтетических представлений вы должны создать прямой импорт проанализированного макета:

import kotlinx.android.synthetic.main.activity_main.*

Таким образом, не существует реальных, немагических сценариев, в которых View был бы нулевым. Если вы не выберете неправильную синтетическую раскладку, но тогда вы получите вылет при первом запуске.

Тем не менее, он, конечно, сломается, если вы измените представление во время выполнения, удалив Views и т. Д. Но опять же, это не используется по умолчанию для синтетического Views и требует другого подхода.

person Kelevandos    schedule 14.03.2018
comment
Что ж, один из реальных жизненных сценариев, где View может быть нулевым, - это создание нового Fragment, а затем вызов представления в этом фрагменте до выполнения метода onCreateView. - person Guy; 14.03.2018
comment
Конечно, но это проблема и с findViewById, так что программист вроде как должен это понимать ^^ - person Kelevandos; 14.03.2018
comment
Спасибо за ответ, технически вы правы, но я не думаю, что это правильный ответ на мой вопрос. Я понял это, найдя другой похожий вопрос SO, ответ на который был именно таким, как я ожидал. Взгляните :) Ответил на свой вопрос. - person Guy; 14.03.2018