Какой тип throw RuntimeException("goodbye")
? Ну, поскольку он никогда не возвращает значение, вы можете использовать его где угодно, независимо от того, какой тип объекта ожидается, и он всегда будет проверять тип. Мы говорим, что он имеет тип Nothing
. Этот тип не имеет значений и является подтипом каждого типа. Следовательно, в notok1
у вас есть звонок на puzzler1<Nothing>
. Приведение из сконструированного TestData
в T = Nothing
внутри puzzler1<Nothing>
нецелесообразно, но не проверяется, и puzzler1
заканчивается возвратом, когда его сигнатура типа говорит, что не должна этого делать. notok1
замечает, что puzzler1
возвратился, когда сказал, что не сможет, и немедленно генерирует исключение. Это не очень наглядно, но я считаю, что причина, по которой он генерирует NPE, заключается в том, что что-то пошло «ужасно неправильно», если функция, которая не может вернуть, вернулась, поэтому язык решает, что программа должна выполнить d ie как можно быстрее.
Для notok2
вы действительно получаете T = TestData
: одна ветвь if
возвращает Nothing
, другая TestData
и LUB из них TestData
(так как Nothing
является подтипом TestData
). notok2
не имеет оснований полагать, что puzzler1<TestData>
не может вернуться, поэтому он не устанавливает ловушку на d ie, как только возвращается puzzler1
.
notok3
имеет практически то же самое проблема как notok1
. Тип возврата, Nothing
, подразумевает, что единственное, что puzzler2<Nothing>
сделает, это выдаст исключение. Таким образом, код обработки сопрограммы в notok3
ожидает, что сопрограмма будет содержать Throwable
и содержит код для его повторного выброса, но не содержит кода для обработки фактического возвращаемого значения. Когда puzzler2
действительно возвращается, notok3
пытается преобразовать это TestData
в Throwable
и терпит неудачу. notok4
работает по той же причине, что и notok2
.
Решение этого беспорядка - просто не использовать неправильное приведение. Иногда puzzler1<T>
/ puzzler2<T>
сможет вернуть T
, если переданная функция фактически возвращает T
. Но, если эта функция выдает, они могут только вернуть TestData
, а TestData
- это , а не a T
(T
- это TestData
, а не наоборот) , Правильная подпись для puzzler1
(и аналогично для puzzler2
):
fun <T : TestData> puzzler1(resultWrapper: (String) -> T): TestData
Поскольку функции являются ковариантными в типе возвращаемого значения, вы можете просто избавиться от параметра типа
fun puzzler1(resultWrapper: (String) -> TestData): TestData