Nie jestem pewien, czy "ciężkie zawody" są właściwym słowem, ale tutaj jest problem, z którym się borykam. I zajęło mi trochę czasu, aby odtworzyć to, aby możliwie najmniejszej przykład, więc o to idzie:Kotlin ciężko zaprzepaszczony w górę do wywnioskowanego (na miejscu) parametru
class BaseParameterizedType<T>
fun <U: BaseParameterizedType<*>> getSpecific(clazz: KClass<in U>) : U {
TODO()
}
fun example(arg: KClass<out BaseParameterizedType<*>>)) {
getSpecific(arg.innerType)
}
Ok, więc powyższy kod nie powiedzie się w „TODO”, ale jeśli to nie było, a jeśli funkcja zwrócona normalnie, to zdecydowanie kończy się niepowodzeniem z wyjątkiem wskaźnika pustego. I starał się dowiedzieć, co się dzieje źle, więc zwróciłem się do dekompilacji kodu Java (od kodu bajtowego Kotlin):
public static final void example(@NotNull KClass arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
getSpecific(arg.getInnerType());
throw null; // <-- The problem
}
Gdybym zmienić podpis funkcję getSpecific(clz: KClass<in U>) : U
do żadnej z tych form:
getSpecific(clz: KClass<out U>) : U
getSpecific(clz: KClass<U>) : U
getSpecific(clz: KClass<in U>) : BaseParameterizedType<*>
lub nawet funkcja do example(arg: KClass<out BaseParameterizedType<*>)
lub example(arg: KClass<BaseParameterizedType<*>>)
, wówczas generowany kod jest:
public static final void example(@NotNull KClass arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
getSpecific(arg.getInnerType());
}
Teraz, powiedzmy, że na wezwanie miejscu, zmienić go na adres:
getSpecific(BaseParameterizedType::class)
wtedy to też nie wygenerować klauzulę throw null
. Zgaduję więc, że ma to coś wspólnego z kotliną zakładając, że ta obsada zawsze zawiedzie lub że dostępne są nieokreślone informacje, aby dokonać wnioskowania?
Tak, wiemy, że arg.innerType
jest KClass<out BaseParameterizedType<*>>
i używamy go w miejscu przyjmującej KClass<in BaseParameterizedType<*>>
, więc dlaczego nie jest U
wywnioskować do BaseParamterizedType<*>>
. To jest dosłownie jedyny typ, który kiedykolwiek będzie pasował.
Jednocześnie, myślę, że wygenerowanie oświadczenia throw null
jest niewiarygodnie trudne do debugowania. Stacktrace po prostu wskazywałby linię, na której znajduje się getSpecific
i powodzenia, wykreślając skąd pochodził wyjątek wskaźnika pustego.
Czy tę powierzchnię emisyjną niedawno? Ponieważ jest to część kodu, który działał idealnie dla mnie, i pękł dopiero niedawno. Przykro mi, że nie będę w stanie wskazać dokładnej wersji kotlin, w której zaczęło się łamać, ponieważ ciągle aktualizujemy wersję i jest to coś, co niestety nie jest objęte naszymi testami i jest rzadko osiągalnym fragmentem kod. Trudno jest więc ustalić, kiedy dokładnie zaczęło się łamać. –