Как ограничить этот подкласс, который не может быть общим?

Ошибка времени компиляции: общий класс не может быть подклассом java.lang.Throwable

public class TestGenericClass<E> extends Exception {

/*Above line will give compile error, the generic class TestGenericClass<E> may 
  not subclass java.lang.Throwable*/

    public TestGenericClass(String msg) {
        super(msg);
    }
}

Ошибка времени компиляции выше по причине, указанной в § jls-8.1.2, как показано ниже, и объясняется в этот вопрос:

Это ошибка времени компиляции, если общий класс является прямым или косвенным подклассом Throwable (§11.1.1).

Это ограничение необходимо, поскольку механизм перехвата виртуальной машины Java работает только с неуниверсальными классами.

Вопрос:

  • Как ограничивается, чтобы подкласс java.lang.Throwable не был универсальным классом?

  • Или будет более общий вопрос: как ограничить подклассы любого класса, которые не могут быть универсальными?


person Vishrant    schedule 03.05.2014    source источник
comment
Я не совсем уверен, используют ли они настоящий инструмент Java. Это может быть просто особый случай, который они включили в компилятор.   -  person christopher    schedule 04.05.2014
comment
Если это так, то как я могу создать свой собственный класс, в котором не будет класса Generic.   -  person Vishrant    schedule 04.05.2014
comment
Тебе нельзя. Это делается в компиляторе. Вы всегда можете расширить компилятор и добавить правило для своего класса.   -  person Boris the Spider    schedule 04.05.2014
comment
Просто попросите ваш класс расширить Throwable, если вы должны позволить компилятору жаловаться, когда кто-то пытается это сделать.   -  person Bhesh Gurung    schedule 04.05.2014
comment
@ Бхеш, я полагаю, тогда у кого-то может возникнуть соблазн его бросить ...   -  person Boris the Spider    schedule 04.05.2014
comment
@BoristheSpider, пожалуйста, уточнить.   -  person Vishrant    schedule 04.05.2014
comment
@BheshGurung мы расширяем до Throwable, когда создаем класс Exception или Error. Это вызовет проблемы, если мне придется расширить класс, например Applet   -  person Vishrant    schedule 04.05.2014
comment
Пожалуйста, не относитесь к этому предложению серьезно. Я не думаю, что у вас здесь очень законный вопрос. Могу я спросить, почему вы хотите это сделать?   -  person Bhesh Gurung    schedule 04.05.2014
comment
@BheshGurung Мне просто любопытно узнать, как все реализовано для java.lang.Throwable   -  person Vishrant    schedule 04.05.2014


Ответы (3)


Как ограничено, что подкласс java.lang.Throwable не будет универсальным классом?

Вот как компилятор OpenJDK выполняет проверку:

import com.sun.tools.javac.code.Symbol.*;   

private void attribClassBody(Env<AttrContext> env, ClassSymbol c) {
    ....

    // Check that a generic class doesn't extend Throwable
    if (!c.type.allparams().isEmpty() && types.isSubtype(c.type, syms.throwableType))
        log.error(tree.extending.pos(), "generic.throwable");

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

Полный исходный код

person Jk1    schedule 03.05.2014
comment
Не могли бы вы добавить название класса и / или какие-либо другие источники? - person Alexey Malev; 04.05.2014
comment
Несмотря на то, что вы взломаны, вы можете предотвратить универсальные подклассы, сделав свой базовый класс расширяемым throwable :) - person Bohemian♦; 04.05.2014
comment
Что произойдет, если кто-то попытается запустить код, написанный на компиляторе, который пропускает эту проверку, на стандартной JVM? - person nanofarad; 04.05.2014
comment
@hexafraction Я не думаю, что ничего другого. Ограничение только для того, чтобы catch не поддерживал универсальные шаблоны во время компиляции. (Все, что связано с дженериками, в любом случае происходит только во время компиляции в Java.) - person Paul Bellora; 04.05.2014
comment
@hexafraction - единственная известная мне спецификация стандартной JVM - это спецификация виртуальной машины Java от Oracle. Эта спецификация не касается случая использования, о котором мы говорим, поэтому я бы сказал, что для стандартной JVM поведение не определено. - person Jk1; 04.05.2014

Как ограничено, что подкласс java.lang.Throwable не будет универсальным классом?

Это было решение написать частный случай в самом компиляторе. Причина этого подробно описана в этом вопросе . По сути, это связано с реифицируемым типом. Вы можете прочитать об этом термине здесь. Короче говоря, тип является реифицируемым, если он полностью доступен во время компиляции. Например, универсальные типы не могут быть преобразованы, потому что их типы удаляются стиранием типа. Объекты, появляющиеся в catch блоке, должны быть реифицируемыми.

Или более общий вопрос: как ограничить подклассы класса, которые не могут быть универсальными?

Есть несколько вариантов ...

В настоящее время в обычных рамках Java нет возможности сделать это. У него нет какой-то final реализации, которая препятствовала бы применению универсальности к подклассам. Как объясняется в комментариях, самое близкое к этому - расширить компилятор и добавить правило специально для вашего класса. Это решение вызывает у меня мурашки по спине. Уклоняйся от этого. Это означает, что ваш код будет работать только с вашей версией Java, и что любой, кто хочет использовать ваш код, должен будет установить ту же версию.

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

person christopher    schedule 03.05.2014
comment
@BoristheSpider Ха-ха, извини. Я знаю, что вы предложили это решение. Честно говоря, как только я увидел расширение компилятора, я немного вздрогнул. Совершенно очевидно, что без обид! - person christopher; 04.05.2014
comment
Это не было серьезным решением. Написание компиляторов следует оставить на усмотрение авторов компиляторов. Препроцессор аннотации сделать довольно просто. Или даже что-то вроде правила Checkstyle. - person Boris the Spider; 04.05.2014
comment
Это, безусловно, допустимые варианты! - person christopher; 04.05.2014

Если вы хотите отложить ошибку до времени выполнения, вы можете использовать отражение в конструкторе (ах) суперкласса, чтобы увидеть, объявляет ли подкласс какие-либо параметры типа.

public class Example {
    public static class ProhibitsGenericSubclasses {
        public ProhibitsGenericSubclasses() {
            if (getClass().getTypeParameters().length > 0)
                throw new AssertionError("ProhibitsGenericSubclasses prohibits generic subclasses (see docs)");
        }
    }

    public static class NonGenericSubclass extends ProhibitsGenericSubclasses {}
    public static class GenericSubclass<T> extends ProhibitsGenericSubclasses {}

    public static void main(String[] args) {
        System.out.println(new NonGenericSubclass());
        System.out.println(new GenericSubclass<Object>());
    }
}

Этот код печатает

Example$NonGenericSubclass@15db9742
Exception in thread "main" java.lang.AssertionError: ProhibitsGenericSubclasses prohibits generic subclasses (see docs)
    at Example$ProhibitsGenericSubclasses.<init>(Example.java:12)
    at Example$GenericSubclass.<init>(Example.java:17)
    at Example.main(Example.java:21)

Если вы хотите запретить параметры типа всех классов в иерархии, а не только самого производного класса, вам нужно пройти вверх по иерархии, используя Class#getSuperclass().

person Jeffrey Bosboom    schedule 08.05.2014