Почему у Котлина нет явной печати? - PullRequest
5 голосов
/ 05 марта 2019

Мне любопытно, почему дизайнеры Kotlin считают, что было бы неплохо отказаться от явной типизации в Kotlin?

Для меня явная типизация не является «болью» при написании на Java (или любом другом языке со строгой типизацией): все IDE могут помочь мне автоматически набрать мою переменную.

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

Кроме того, и это моя главная проблема, это делает код более подверженным ошибкам.

Пример:
Java: легко идентифицируется как строка, все хорошо

String myPrice = someRepository.getPrice(); // Returns a String
myTextView.setText(myPrice); 

Java: легко идентифицируется как int, запах кода с setText()

int myPrice = someRepository.getPrice(); // Returns an int, code smell !!
myTextView.setText(String.valueOf(myPrice)); // Problem avoided

Котлин: ????

val myPrice = someRepository.getPrice(); // What does it return ?
myTextView.setText(myPrice); // Possible hidden bug with setText(@StringRes int) instead of setText(String) !!

Отсутствие явной печати в Kotlin - самый большой недостаток Kotlin imo. Я пытаюсь понять этот выбор дизайна.

Я на самом деле не ищу "патчи" для исправления примера / чтобы избежать запаха представленного кода, я пытаюсь понять основную причину удаления явной типизации. Это должно быть больше, чем «меньше печатать / легче читать». Он удаляет только пару символов (по-прежнему нужно писать val / var), а явная печать в Kotlin все равно добавляет несколько символов ...

Статическая типизация без явной типизации, на мой взгляд, является наихудшим сценарием для борьбы со скрытыми ошибками / ошибками спагетти: если один класс (скажем, «Репозиторий») меняет тип возвращаемого значения (с String на int для пример). При явной типизации компиляция не удалась бы в классе, вызывающем «Репозиторий». Без явной типизации компиляция может не завершиться неудачей, и неверный тип переменной может «перемещаться» по классам и изменять поведение классов из-за своего типа. Это опасно и не обнаружено.

Исправить несложно; явно введите переменную. Но мы говорим на Kotlin, языке, созданном для любителей игры в код: люди не будут явно набирать свои переменные, так как в kotlin это занимает даже больше времени, чем в turtle-java. Хлоп!

Ответы [ 2 ]

15 голосов
/ 05 марта 2019

Прежде всего: у Kotlin static typing.Компилятор точно знает, какой тип идет или куда приходит.И вы всегда можете это записать, как

fun sum(a: Int, b: Int): Int {
  val c: Int = 1

Суть в том, что вы можете написать много кода, который просто полагается на тот факт, что все, что вы делаете, статически типизировано.и тип проверен тоже!

Вам действительно нужно знать, добавляете ли вы два двойных, два целых или два длинных, когда все, что вам «нужно» посмотреть, это a + b?

В конце концов, речь идет о балансировке различных требований.Оставление типа может быть полезным: меньше кода, который должен быть прочитан и понят читателями.

Конечно: если вы пишете код, чтобы люди постоянно обращались к своей IDE за IDE, чтобы сообщить им фактический, предполагаемый тип чего-либо, то вы превратили полезную функцию в проблему!

Реальный ответ здесь: это зависит.Я был во многих обзорах кода, где C ++ люди обсуждали использование ключевого слова auto.Было много ситуаций, когда использование auto действительно облегчало чтение и понимание кода, потому что вы могли бы сосредоточиться на «что происходит с переменной», вместо того, чтобы смотреть 1 минуту на ее объявление, пытаясь понять его тип,Но были также случайные примеры, когда auto достигали полной противоположности, и легче было следовать «полностью типизированной» декларации.

И учитывая комментарий ОП: вы должны знать, что вы делаете в первую очередь.Значение: когда вы пишете код, вы не просто вызываете «любой» метод, который вы найдете в каком-либо объекте.Вы вызываете метод, потому что вы должны.Вы лучше знаете , что он делает, и что он возвращает вам.Я согласен, когда кто-то спешит, вы быстро назначаете var, а затем передаете эту вещь, что может привести к ошибкам.Но для каждой ситуации, когда var помогает в создании ошибки, может быть 10 случаев, когда это помогает писать более простой для чтения код.Как сказано: жизнь - это баланс.

Наконец: языки не должны добавлять функции без причины.И люди Kotlin тщательно уравновешивают особенности, которые они добавляют.Они не добавили вывод типа, потому что он есть в C ++.Они добавили это, потому что они тщательно исследовали другие языки и нашли это полезным, чтобы быть частью языка. Любая функция языка может быть использована не по назначению. всегда зависит от программиста, чтобы написать код, который легко читать и понимать.И когда ваши методы имеют неясные сигнатуры, поэтому «чтение» их имен само по себе не говорит вам о том, что происходит с гонгом, то вините это в имени метода, а не в выводе типа!

6 голосов
/ 05 марта 2019

Цитируя Java Вывод типа локальной переменной JEP :

В цепочке вызовов вроде:

int maxWeight = blocks.stream()
                    .filter(b -> b.getColor() == BLUE)
                    .mapToInt(Block::getWeight)
                    .max();

никто не беспокоится (или дажезамечает), что промежуточные типы Stream<Block> и IntStream, а также тип лямбда-формального b явно не указаны в исходном коде.

Вас это беспокоит??

если один класс (скажем, «Репозиторий») меняет тип возвращаемого значения (например, с String на int).При явной типизации компиляция не удалась бы в классе, вызывающем «Репозиторий».

Если в вашем примере имеются перегрузки, например setText, то

Repository repository = ...;
myTextView.setText(repository.getFormerlyStringNowInt());

также не завершится без вывода типа.Чтобы это не сработало, ваш стандарт кода должен требовать присваивания результата каждой операции локальной переменной, как в

Stream<Block> stream1 = blocks.stream();
Predicate<Block> pred = b -> { 
    Color c = b.getColor();
    return c == BLUE;
};
Stream<Block> stream2 = stream1.filter(pred);
ToIntFunction<Block> getWeight = Block::getWeight;
IntStream stream3 = stream2.mapToInt(getWeight);
int maxWeight = stream3.max();

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

Наконец, Kotlin не был создан в вакууме: дизайнеры увидели, что когда C # ввел локальный вывод типов в 2007 году, это не привело к значительным проблемам,Или они могли бы посмотреть на Scala, которая была у нее с начала 2004 года;у него было (и есть) множество жалоб пользователей, но локальный вывод типов не входит в их число.

...