Почему компиляция этого кода вызывает переполнение стека компилятора? - PullRequest
11 голосов
/ 23 ноября 2011
interface Pong<T> {}
class Ping<T> implements Pong<Pong<? super Ping<Ping<T>>>> {
    static void Ping() {
        Pong<? super Ping<Long>> Ping = new Ping<Long>();
    }
}

Попытка скомпилировать это приводит к ошибке:

The system is out of resources.
Consult the following stack trace for details.
java.lang.StackOverflowError
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    at com.sun.tools.javac.code.Types$UnaryVisitor.visit(Types.java:3260)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2592)
    at com.sun.tools.javac.code.Types$23.visitClassType(Types.java:2579)
    at com.sun.tools.javac.code.Type$ClassType.accept(Type.java:554)
    ...

Код предоставлен etorreborre на github.

Ответы [ 4 ]

6 голосов
/ 23 ноября 2011

Поскольку компилятор не может решить, является ли Long Pong, то есть super из Ping из Ping из Long или это Ping из Ping чего-то, что расширяет Pong от Pong ... но я могу ошибаться.

6 голосов
/ 23 ноября 2011

Очевидно, это ошибка в компиляторе Java.Компилятор не должен аварийно завершать работу, особенно в такой маленькой программе.

Он может даже стать пробелом в спецификации языка Java;то есть неясный крайний случай в дженериках, который авторы JLS не рассматривали.

Но (IMO) это не более чем любопытство, если только вы не можете придумать пример, который не так очевиден Придумал сломать компилятор.Я имею в виду, этот пример кода не имеет особого смысла ...


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

2 голосов
/ 10 января 2012

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

По сути, код выглядит примерно так:

public abstract class AImpl<X extends A<Y>, Y extends A<X>> {
    public X foo(Y o) {
        return o.doStuff();
    }

    public Y bar(X o) {
        return o.doStuff();
    }
}

class VImpl extends AImpl<V, E> {}
class EImpl extends AImpl<E, V> {}

interface A<T> {
    T doStuff();
}

interface V extends A<E> {}
interface E extends A<V> {}

Этот код фактически компилируется. В действительности существует не только 2 подкласса, но и более глубокая иерархия типов, например, три варианта VImpl и EImpl, каждый из которых имеет произвольное множество подклассов. Ну, и на самом деле, есть 3 типа параметров, и ограничения немного сложнее, как показано выше.

Когда было только два варианта VImpl и EImpl, он все равно компилировался. Как только были добавлены третьи варианты, он получил переполнение стека в компиляторе. Тем не менее, компилятор Eclipse по-прежнему способен компилировать код, поэтому кажется, что он просто javac делает что-то рекурсивное, что лучше делать итеративно.

К сожалению, мы не можем сократить полную базу кода до некоторого минимального примера, подходящего для сообщения об ошибке ...

0 голосов
/ 13 января 2016

Я испытал то же самое с некоторыми общими вещами в JDK 8_u25, обновление до JDK 8u_65 решило проблему.

...