Комментарии 20
— Хочется поговорить о проблемных местах. Продолжая разговор про обратную совместимость: а было ли за годы работы над Kotlin принято какое-то значимое решение, о котором впоследствии пожалели, но было уже поздно менять?
— Да, бывало. Этот вопрос нередко задают, и у меня уже есть заготовленный ответ. На самом деле, мне надо почаще думать про это заново, но пока просто расскажу имеющийся ответ. Есть две наиболее сложных в поддержке фичи, про которые я жалею, что мы либо вообще их зарелизили, либо не подумали над ними в своё время побольше.
Одна — это делегирование классов, возможность имплементировать интерфейсы делегированием. Хорошо было бы, если бы мы не зарелизили это в своё время в 1.0 и взяли побольше времени на то, чтобы это подизайнить. В 1.0 у нас ещё не было понятия экспериментальных фич. В итоге получилась очень негибкая фича, и очень сложно как-то её развить. Мы в этой области не сделали никаких позитивных шагов за два года, потому что это попросту слишком сложно, она сопротивляется. И что с этим делать, пока непонятно, будем думать дальше, как сдвинуться с этой мёртвой точки.
И вторая такая же проблемная фича — это companion objects. Там развитие какое-то есть, но это тоже слишком сложная в поддержке история, потому что много завязок в разные места. Было бы здорово, если бы мы в своё время потратили побольше времени на дизайн этой фичи. Но мы не догадались, что это такая серьёзная точка риска, и не вложили туда больше ресурсов.
Ещё есть доклад про заимствованные из других языков идеи
Однажды я писал примерно вот такой код:
val handler = Handler()
val action = { println("Hello world!!!") }
handler.post(action)
handler.removeCallbacks(action)
handler.post(Runnable)
добавляет Runnable в очередь на выполнение, а handler.removeCallbacks(Runnable)
удаляет Runnable из этой очереди. Судя по коду, строка Hello world!!!
не будет напечатана, но она печатается. В чем же дело?
Оказывается лямбда имеет тип Function0<Unit>
, а handler.post(Runnable)
и handler.removeCallbacks(Runnable)
принимают в качестве аргумента Runnable
, и котлин для удобства и бесшовной интегреции с Java оборачивает лямбду в Runnable
и получается, что в очередь добавляется один Runnable
, а удаляется другой.
Для того, что бы исправить ситуацию, нужно добавить Runnable
перед лямбдой, вот так: val action = Runnable { println("Hello world!!!") }
. Теперь action
имеет тип Runnable
и ничего не оборачивается и корректно работает.
Получается, что в данном случае проблема в том, что компилятор много на себя берет и скрывает часть своей работы от программиста с расчетом на то, что этот сахар ничего не сломает.
У меня, честно говоря, в голове не очень укладывается: если я вот прямо сейчас начну делать язык программирования, да ещё и "наберу команду" — моих сбережений хватит в лучшем случае месяца на 3 работы. Откуда у Бреслава такие ресурсы?
КДПВ почему не с Kotlin? ;)
О, удачный код для обсуждения Котлина. myself
в данной функции имеет тип Person?
. Хотя скорее всего подразумевается, что Я уж точно существую. Как идеоматичнее быть в данном случае? Не хотелось бы использовать оператор !!
, т.к. его использование расслабляет и плявляется соблазн использовать его повсеместно, что сводит на нет все плюсы котлина с точки зрения null безопасности. Можно написать ?: return
, но это так же плохо как и пустой блок catch
при обработке исключений. Наверное правильнее было бы бросить исключение: ?: throw IllegalStateException("Myself is not exists?!")
, но не слишком ли много писанины ради ситуации, которая никогда должна случиться?
Кстати, если не менять код, то we
будет иметь тип List<Person?>
, что выглядит так же странно.
Все так, если бы не слишком важен был бы эксепшен — я бы просто использовал first
.
Return может иметь смысл в зависимости от контекста.
Ну и самый общий случай это кидать после элвиса свой "бизнес" exception.
Делать методы которые принимают Person?
в данном случае конечно не стоило
throw IllegalStateException(...) можно записать короче — error(...). Функция именно это исключение и бросает
Разве что после Java не хватает тернарного оператора.
Мучают ли Андрея Бреслава ошибки в дизайне Kotlin, которые уже нельзя исправить// Мы обречены #6