Почему Java не делает вывод типов?

44

Я всегда удивлялся, почему Java не делает вывод типов, учитывая, что язык такой, какой он есть, и его виртуальная машина очень зрелая. Google Go - это пример языка с превосходным выводом типов, который уменьшает количество набираемых текстов. Есть ли какая-то особая причина того, что эта функция не является частью Java?

Cobie
источник
3
правила обратной совместимости на языке, таком старом и популярном, как java
ratchet freak
9
@ratchetfreak Может ли вывод типа не быть добавлен обратно-совместимым способом? Старые программы будут просто предоставлять больше информации о типе, чем необходимо.
1
Это может иметь некоторые непреднамеренные эффекты при использовании со стиранием типа. docs.oracle.com/javase/tutorial/java/generics/…
Захари Йейтс
4
Обратите внимание, что Java 8 принесет много выводов типов благодаря своей функции Lambda: вы можете писать сложные лямбды без упоминания каких-либо типов и всего, что выводится.
Йоахим Зауэр
4
Вывод типа локальной переменной приходит в Java: JEP 286:
Джимми Пейдж

Ответы:

60

Технически говоря, Java имеет вывод типов при использовании обобщений. С помощью общего метода, такого как

public <T> T foo(T t) {
  return t;
}

Компилятор проанализирует и поймет это при написании

// String
foo("bar");
// Integer
foo(new Integer(42));

String будет возвращен для первого вызова и Integer для второго вызова на основе того, что было введено в качестве аргумента. В результате вы получите правильную проверку во время компиляции. Кроме того, в Java 7 можно получить дополнительные выводы при создании экземпляров обобщенных типов, например

Map<String, String> foo = new HashMap<>();

Java достаточно любезен, чтобы заполнить пустые угловые скобки для нас. Теперь, почему Java не поддерживает вывод типов как часть назначения переменных? В какой-то момент был RFE для вывода типа в объявлениях переменных, но это было закрыто как "Не будет исправлено", потому что

Люди извлекают выгоду из избыточности объявления типа двумя способами. Во-первых, избыточный тип служит ценной документацией - читателям не нужно искать объявление getMap (), чтобы выяснить, какой тип он возвращает. Во-вторых, избыточность позволяет программисту объявлять предполагаемый тип и, таким образом, извлекать выгоду из перекрестной проверки, выполняемой компилятором.

Автор, который закрыл это, также отметил, что это просто «не похоже на java», с чем я согласен. Многословие Java может быть и благословением, и проклятием, но оно делает язык таким, какой он есть.

Конечно, этот конкретный RFE не был концом этого разговора. Во время Java 7 эта возможность снова обсуждалась , при этом были созданы некоторые тестовые реализации, в том числе сам Джеймс Гослинг. Опять же, эта функция была в конечном итоге сбита.

С выпуском Java 8 мы теперь получаем вывод типа как часть лямбда-выражений как таковых:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

Компилятор Java , может смотреть на метод , Collections#sort(List<T>, Comparator<? super T>)а затем интерфейс Comparator#compare(T o1, T o2)и определить , что firstи secondдолжно быть , Stringтаким образом , позволяя программисту отказаться от того , чтобы пересчитать тип в лямбда - выражении.

острая боль
источник
5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- да, если это так, HashMap<String, Integer> map = foo.getMap()я согласен - в C # я обычно не использую varв таких случаях, хотя я мог бы. Но этот аргумент не выдерживает критики, если это так HashMap<String, Integer> map = new HashMap<String, Integer>(). Это настоящая избыточность, и я не вижу смысла в том, чтобы писать имя типа дважды. И как бы мне здесь пригодилась перекрестная проверка компилятора, я вообще не понимаю.
Конрад Моравский
9
Да, и это хорошо для дженериков, но я все еще скучаю по эквиваленту C # varв Java.
Конрад Моравский
15
Что касается «не-java-подобного», эта (глупая) история приходит на ум;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Конрад Моравский
6
Кроме того, тип возвращаемого значения функции часто бывает очевидным или ненужным, и даже в тех случаях, когда это не ваша среда IDE, можно пометить тип за полсекунды.
Phoshi
8
Я считаю, что официальная цитата Humans benefit from the redundancyявляется реальным ответом на текущий вопрос, потому что каждое оправдание, которое я прочитал, кажется беспричинным, необоснованным и нелепым оправданием от разработчиков Java: и C #, и C ++ имеют особенность, дизайнеры C # и C ++ не менее компетентны, чем Java, и все счастливы, используя его. Что может отличить разработчиков Java от разработчиков на C # или C ++? Поэтому я согласен с @KonradMorawski, «Это то , как вещи делаются здесь» , кажется , снова быть реальным за кадром причина.
paercebal
16

Ну, во-первых, вывод типа не имеет никакого отношения к зрелости среды выполнения, будь то 30-летний процессор или виртуальная машина, которая настолько нова, что биты все еще блестящие. все дело в компиляторе.

Тем не менее, это разрешено для обобщенных типов, причина, почему это не разрешено для неуниверсальных типов, по-видимому, из-за философии - ничто не мешает дизайнерам добавить его.

Обновление: похоже, что java 10 поддерживает это - http://openjdk.java.net/jeps/286

jmoreno
источник
5

Насколько я знаю, когда Java была разработана в начале девяностых, вывод типа не был настолько популярен среди основных языков (но это уже была очень хорошо известная концепция, например, в ML). Итак, я могу себе представить, что вывод типов, вероятно, не был поддержан, потому что Java была нацелена на программистов, пришедших из C ++, Pascal или других основных языков, у которых этого не было (принцип наименьшего удивления).

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

Я не знаю, получит ли Java вывод типов в будущем, но IMO, что станет большой революцией для языка (как упомянул Гленн Нельсон, это было описано как «не-java-подобный»), и тогда можно было бы также рассмотреть возможность удаления Назовите Java в пользу нового имени.

Если вы хотите использовать язык JVM с выводом типа, вы можете использовать Scala.

Джорджио
источник
2

Я могу придумать несколько возможных причин. Во-первых, явная типизация является самодокументированием. Java обычно делает это приоритетом над краткостью. Другая причина может быть в случаях, когда тип является несколько двусмысленным. Например, когда тип или любой подтип может удовлетворить процедуру. Допустим, вы хотите использовать List, но кто-то приходит и использует метод, эксклюзивный для ArrayList. JIT выведет ArrayList и продолжит работу, даже если вы захотите скомпилировать ошибку.

Jiggy
источник
8
Как работает GridBagLayout gridbag = new GridBagLayout (); добавить к себе документацию? Это чистое повторение.
Андерс Линден
3
Это устраняет неоднозначность в случае, когда два типа не совпадают. Вы можете так же легко назначить экземпляр подкласса.
Джигги
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- Я не понимаю этого немного. Вывод типа происходит в момент создания переменной, а не вызова метода для нее. Не могли бы вы показать пример того, что вы имели в виду?
Конрад Моравский
2
@KonradMorawski: Я предполагаю, что он имеет в виду, что если метод возвращает ArrayList, тип будет выведен на это. Если вы хотите трактовать тип как что-то отличное от возвращаемого типа, вы не можете использовать логический вывод. Я не понимаю, как это может быть проблемой в кодовой базе где-то близко к здравому смыслу.
Phoshi
JIT не будет играть никакой роли в выводе типа. вывод типа является явлением времени компиляции. и если переменная объявлена ​​как ссылка на список, попытка доступа к членам ArrayList для нее не приведет к проверке типа, и вы получите ошибку компиляции
sara
0

Это противоречит устоявшейся рекомендации объявлять переменные с использованием наиболее общего интерфейса, который будет соответствовать вашим потребностям, и инициализировать их соответствующим классом реализации, как в

Collection<String> names = new ArrayList<>();

Эффективно,

var names = new ArrayList<String>();

ничего, кроме синтаксического сахара для

ArrayList<String> names = new ArrayList<String>();

Если вы хотите, ваша IDE может создать его из new ArrayList<String>()выражения «одним щелчком» (рефакторинг / создание локальной переменной), но помните, что это противоречит рекомендации «использовать интерфейсы».

Ральф Клеберхофф
источник