ExceptionInInitializerError "Параметр, указанный как ненулевой, является нулем" с абстрактным классом - PullRequest
0 голосов
/ 22 мая 2019

Мне нужно рассчитать hash жестко закодированных изображений.

abstract class ImageData {
    protected abstract val master: List<String>
    val data: Iterable<HexString> = master.map { s -> hex(s) }
    val hash: Int by lazy {
        master.fold(0) { hash, s ->
            31 * hash + s.hashCode()
        }
    }
}

Пример изображения.

object FooImageData : ImageData() {
    override val master = listOf(
        "424d3684030000000000..."
        // ...
    )
}

Исключение:

java.lang.ExceptionInInitializerError
    at ....updateGraphics(Graphics.kt:162)
...
 Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter $this$collectionSizeOrDefault
    at kotlin.collections.CollectionsKt__IterablesKt.collectionSizeOrDefault(Iterables.kt)
    at ....ImageData.<init>(ImageData.kt:17)
    at ....FooImageData.<init>(FooImageData.kt:3)
    at ....FooImageData.<clinit>(FooImageData.kt:3)
    at ....updateGraphics(Graphics.kt:162)

в ....updateGraphics(Graphics.kt:162):

private suspend fun updateGraphics(...) {
    val hash = (FooImageData.hash * 31 + BarImageData.hash)

Удаление lazy не устраняет проблему.

Все исследования показывают, что упорядочение параметров может быть проблемой, но здесь, похоже, дело не в этом - или это так?

Использование:

abstract class ImageData {
    abstract val master: List<String>
    // Yes I know the `get()` is unnecessary but for some weird reason that causes `hash` to crash.
    val data: Iterable<HexString> get() = master.map { s -> hex(s) }
    val hash: Int by lazy {
        master.fold(0) { hash, s ->
            31 * hash + s.hashCode()
        }
    }
}

кажется, решил проблему - не знаю почему.

версия Kotlin Latest stable (1.3)

Целевая версия JVM: 1.6

1 Ответ

1 голос
/ 24 мая 2019

Я думаю, что ключевое отличие - get() в свойстве data в сочетании с тем фактом, что master является абстрактным.Когда этот базовый класс создается (то есть до , создается подкласс, так как конструктор подкласса должен вызывать конструктор суперкласса прежде всего), базовый класс инициализирует все его члены.Ваш исходный код имел следующую строку:

val data: Iterable<HexString> = master.map { s -> hex(s) }

Получает значение master, которое на данный момент равно нулю, так как конкретный подкласс еще не создан, поэтому еще не может переопределить свойство.

В обновленном фрагменте у вас есть следующее:

val data: Iterable<HexString> get() = master.map { s -> hex(s) }

Свойство data теперь не нужно инициализировать (используя значение master) во время инициализацииабстрактный базовый класс.Вместо этого, когда свойство data вызывается во время выполнения, будет выполняться функция get.К тому времени конкретный подкласс уже создан и может предоставить правильное значение для master.

. Более подробная информация об этом содержится в документации , где говорится:

При разработке базового класса следует избегать использования открытых членов в конструкторах, инициализаторах свойств и блоках инициализации.

(Свойство master равно abstract, что означаетэто open.)

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