Подтвердить что ты не робот

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

Ошибка времени компиляции: Общий класс может не подклассов 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).

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

Вопрос:

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

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

4b9b3361

Ответ 1

Как это ограничено, что подкласс 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");

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

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

Ответ 2

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

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

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

Ну, есть несколько вариантов.

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

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

Ответ 3

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

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>());
    }
}

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

[email protected]
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().