Из того, что я прочитал: причина в том, что нелегко определить, какой метод на самом деле будет вызываться, поскольку у нас есть наследование.
Однако почему в Java, по крайней мере, нет оптимизации хвостовой рекурсии для статических методов и не реализован надлежащий способ вызова статических методов с помощью компилятора?
Почему Java вообще не поддерживает хвостовую рекурсию?
Я не уверен, есть ли здесь какие-либо трудности.
Что касается предложенного дубликата , как объяснил Йорг Миттаг 1 :
- Другой вопрос спрашивает о TCO, этот о TRE. TRE намного проще, чем TCO.
- Кроме того, другой вопрос задает вопрос о том, какие ограничения накладывает JVM на языковые реализации, которые хотят компилировать в JVM, этот вопрос задается о Java, который является единственным языком, который не ограничен JVM, поскольку спецификация JVM может быть изменена с помощью те же люди, которые проектируют Java.
- И, наконец, в JVM нет даже ограничений на TRE, потому что у JVM есть GOTO внутри метода, и это все, что нужно для TRE.
1 Добавлено форматирование, чтобы вызвать сделанные пункты.
Ответы:
Как объясняет Брайан Гетц (Java Language Architect в Oracle) в этом видео :
Все, что изменило количество кадров в стеке, сломало бы это и вызвало бы ошибку. Он признает, что это глупая причина, и поэтому разработчики JDK заменили этот механизм.
Затем он упоминает, что это не приоритет, а хвостовая рекурсия.
NB Это относится к HotSpot и OpenJDK, другие виртуальные машины могут отличаться.
источник
В Java нет оптимизации хвостового вызова по той же причине, что и в большинстве императивных языков ее нет. Императивные циклы являются предпочтительным стилем языка, и программист может заменить хвостовую рекурсию императивными циклами. Сложность не стоит того, чтобы использовать функцию, которая не рекомендуется из-за стиля.
Эта вещь, когда программисты хотят иногда писать в стиле FP на других императивных языках, вошла в моду только в последние 10 лет или около того, после того, как компьютеры начали масштабировать ядра вместо ГГц. Даже сейчас это не так популярно. Если бы я предложил заменить императивный цикл на хвостовую рекурсию на работе, половина обозревателей кода рассмеялась бы, а другая половина смутно посмотрела бы. Даже в функциональном программировании вы обычно избегаете хвостовой рекурсии, если другие конструкции, такие как функции высшего порядка, не подходят точно.
источник
for
цикл» (и аналогично для первоклассных функций против объектов). Я бы не стал так называть это «императивным фанатизмом», но я не думаю, что он был бы очень востребован еще в 1995 году, когда главной заботой, вероятно, была скорость Java и отсутствие обобщений.В Java нет оптимизации высоких вызовов, потому что у JVM нет байт-кода для хвостовых вызовов (для некоторого статически неизвестного указателя функции, например, метода в некоторой виртуальной таблице).
По социальным (и, возможно, техническим) причинам, похоже, что добавление новой операции с байт-кодом в JVM (что сделало бы ее несовместимой с более ранними версиями этой JVM) является чрезвычайно трудным для владельца спецификации JVM.
Технические причины, по которым не следует добавлять новый байт-код в спецификацию JVM, включают тот факт, что реальные реализации JVM являются ужасно сложными программными компонентами (например, из-за множества JIT-оптимизаций, которые он выполняет).
Хвостовые вызовы некоторой неизвестной функции требуют замены текущего кадра стека новым, и эта операция должна находиться в JVM (это не только вопрос изменения компилятора, генерирующего байт-код).
источник
Если язык не имеет специального синтаксиса для выполнения хвостового вызова (рекурсивного или иного) и компилятор будет кричать, когда хвостовой вызов запрашивается, но не может быть сгенерирован, «дополнительная» хвостовая вызов или оптимизация хвостовой рекурсии приведут к ситуациям, когда кусок кода может потребовать менее 100 байт стека на одном компьютере, но более 100 000 000 байт стека на другом. Такое различение следует считать качественным, а не просто количественным.
Ожидается, что машины могут иметь разные размеры стеков, и поэтому всегда возможно, чтобы код работал на одной машине, но перебил стек на другой. Как правило, однако, код, который будет работать на одной машине, даже когда стек искусственно сужен, вероятно, будет работать на всех машинах с «нормальными» размерами стека. Однако, если метод, который рекурсивно использует глубину 1 000 000, оптимизирован с помощью хвостового вызова на одной машине, но не на другой, выполнение на первой машине, вероятно, будет работать, даже если его стек необычно мал, и завершится неудачей на последней, даже если его стек необычно большой ,
источник
Я думаю, что рекурсия хвостового вызова не используется в Java в основном потому, что это может изменить трассировку стека и, следовательно, значительно усложнит отладку программы. Я думаю, что одна из основных целей Java - дать возможность программистам легко отлаживать свой код, и для этого требуется трассировка стека, особенно в среде объектно-ориентированного программирования. Поскольку вместо этого можно было использовать итерацию, языковой комитет решил, что не стоит добавлять хвостовую рекурсию.
источник