Использование расширений и интерфейсов Kotlin Nullable для Android (Java) - PullRequest
0 голосов
/ 09 ноября 2018

Я пытаюсь освоить «родной путь Kotlin» для работы в Android, будучи экспертом ни в Kotlin, ни в Java, ни в разработке Android. В частности, как правильно обращаться с обнуляемостью в интерфейсах / расширениях Android .

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

адаптер:

Во-первых, класс Адаптер:

class PersonListAdapter(private val list: ArrayList<Person>,
                        private val context: Context) : RecyclerView.Adapter<PersonListAdapter.ViewHolder>() {

Примечание: в этом примере использует ArrayList<> вместо ArrayList<>?.

Основная деятельность:

Затем в основном упражнении он имеет следующий фрагмент:

private var adapter: PersonListAdapter? = null
private var personList: ArrayList<Person>? = null
private var layoutManager: RecyclerView.LayoutManager? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    personList = ArrayList<Person>()
    layoutManager = LinearLayoutManager(this)
    adapter = PersonListAdapter(personList!!, this)

Обратите внимание на две вещи: (1) personList - это ArrayList<>? (обратите внимание на обнуляемость) , и (2) список позже вызывается с personList!! по сути избавляет нас от цели безопасного использования обнуляемых объектов в Kotlin.


Опция ??

Я не понимаю, почему автор сделал это. Почему бы просто не присвоить список чему-то ненулевому изначально? Например, что-то вроде:

private var personList = ArrayList<Person>()

Или, если это не может быть легко сделано, почему бы не инициализировать адаптер с обнуляемым списком? Я предполагаю , последнее не может быть сделано, потому что мы расширяем RecyclerView.Adapter, но я хотел спросить, чтобы быть уверенным. Я думаю что-то вроде следующего:

class PersonListAdapter(private val list: ArrayList<Person>?,
                        private val context: Context) : RecyclerView.Adapter<PersonListAdapter.ViewHolder>() {

(обратите внимание на ArrayList<Person>? вместо ArrayList<Person>.)


Или есть еще лучшая парадигма?

Резюме

Как правильно обращаться с обнуляемостью в Kotlin при работе с интерфейсами Android, расширениями и другими ситуациями, когда мы опираемся на глубокую базу Java-кода?

Я бы действительно хотел бы по возможности избегать использования !!. Хотя я новичок в этой области, мне кажется, что смысл ? состоит в том, чтобы избежать ошибки в миллиард долларов , и что использование !! в сущности говорит: "Нет, я хочу эта ошибка в миллиард долларов назад, не так ли?

1 Ответ

0 голосов
/ 09 ноября 2018

Либо автор этого кода просто не думал, либо они также новичок в Kotlin.

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

Теперь, как правило, лучший способ обработать переменную поздней инициализации с помощью модификатора lateinit:

private lateinit var personList: ArrayList<Person>

, а затем создать его экземпляр в onCreate(). Он больше не может обнуляться, поэтому для него не нужен модификатор !!.

Конечно, если вы хотите сохранить текущий обнуляемый статус, вы всегда можете просто установить параметр в конструкторе Адаптера на обнуляемый. Вам просто нужно сделать что-то интересное с самим адаптером:

getItemCount(): Int = list?.size!! //or list!!.size

val item = list?.get(position)
item?.whatever

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


Честно говоря, !! здесь на самом деле не принимает эту "ошибку в миллиард долларов" назад. Список инициализируется прямо над personList!!, поэтому, если у вас не запущен еще один Поток, который может сделать personList нулевым, прежде чем ваш основной Поток достигнет инициализации Адаптера, personList гарантированно будет ненулевым.


Что касается "лучшего" способа справиться с нулевой безопасностью ... ну, его нет. Я бы сказал, что «лучшим» способом в этом случае было бы что-то вроде этого:

private val personList = ArrayList<Person>() //this needs to be at the top now
private val adapter by lazy { PersonListAdapter(personList, this) } //by lazy will initialize this variable once it's first referenced and then retain that instance for future calls
private val layoutManager by lazy { LinearLayoutManager(this) }

Теперь все заботится о том, когда класс инициализируется (или когда на переменную ссылаются впервые), и вам не нужно беспокоиться об обнуляемости.


У меня есть пример того, где структура вашего цитируемого кода может вступить в игру: singletons.

class SomeSingleton private constructor() {
    companion object {
        private var instance: SomeSingleton? = null

        fun getInstance(): SomeSingleton {
            if (instance == null) instance = SomeSingleton()
            return instance!! //since technically instance could've been made null from another Thread, Kotlin requires that you assert the non-null state
        }
    }
}

Так на самом деле работает by lazy, если вы посмотрите на декомпилированный проект Kotlin JVM. Конечно, он не будет внутри своего собственного класса, но Kotlin создаст глобальную переменную и метод getVariable(), который будет делать то же, что и метод getInstance(), который я описал выше.

Возможно, все это можно заменить на:

class SomeSingleton private constructor() {
    companion object {
        val instance by lazy { SomeSingleton() }
    }
}

TL; DR, я могу только строить догадки, но кажется, что автор придерживается некоторых стилей программирования Java, либо забывая, не осознавая, либо отказываясь использовать альтернативы Kotlin.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...