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

Вызывающая база и производные статические методы переменной типа

У меня есть следующий пример:

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        A<ConcreteErrorHandler> a = new A<ConcreteErrorHandler>();
        a.m(); //Exception here!
    }

    public static class AbstractErrorHandler {
        public static void handle(){ 
            throw new UnsupportedOperationException("Not implemented");
        }
    }

    public static class ConcreteErrorHandler extends AbstractErrorHandler{
        public static void handle(){ 
            System.out.println("Concrete handler");
        }
    }

    public static class A<T extends AbstractErrorHandler>{
        public void m(){
            T.handle();
        }
    }
}

IDEONE

Почему метод базового класса вызывается, но не производный? Подписи методов handle() совершенно одинаковы. Я знаю, что статические методы не наследуют, но не должны ли возникать ошибка компиляции в моем случае?

Может ли кто-нибудь объяснить это поведение?

4b9b3361

Ответ 1

Причиной этого является то, что компилятор не знает, который точный подтип AbstractErrorHandler будет заменять T на Runtime. Поэтому он просто связывает вызов метода T.handle() с методом AbstractErrorHandler.handle().

Проблема заключается в том, что вы смешиваете наследование с static функциями классов в Java.

Чтобы это работало (правильно), вам нужно избавиться от модификатора static для методов .handle() и сохранить экземпляр T в классе A. Этот экземпляр T (в Runtime) будет отдельным подклассом AbstractErrorHandler, а затем будет выполнен фактический .handle() метод.

Например:

class Ideone {
    public static void main(String[] args) throws java.lang.Exception {
        A<ConcreteErrorHandler> a = new A<ConcreteErrorHandler>(new ConcreteErrorHandler());
        a.m();
    }

    public static class AbstractErrorHandler {
        public void handle() {
            throw new UnsupportedOperationException("Not implemented");
        }
    }

    public static class ConcreteErrorHandler extends AbstractErrorHandler {
        public void handle() {
            System.out.println("Concrete handler");
        }
    }

    public static class A<T extends AbstractErrorHandler> {

        T instance;

        A(T instance) {
            this.instance = instance;
        }

        public void m() {
            instance.handle();
        }
    }
}

Ответ 2

4.4. Переменные типа говорят нам, что:

Элементы переменной типа X с bound T & I1 & ... & In являются членами типа пересечения T & I1 & ... & In , появляющимся в точке, где объявлена ​​переменная типа.

Поэтому члены T extends AbstractErrorHandler являются членами AbstractErrorHandler. T.handle(); относится к AbstractErrorHandler.handle();.

Ответ 3

Стирание параметра ограниченного типа является границей (и в случае связанного пересечения - первого типа в границе). Поэтому в вашем случае T extends AbstractErrorHandler стирается до AbstractErrorHandler, и ваш метод эффективно заменяется на:

 public void m() { AbstractErrorHandler.handle(); }

См. например JLS 4.6

Стирание переменной типа (§4.4) является стиранием ее левой части.

Ответ 4

Потому что в основном ваш метод m будет скомпилирован в

public void m(){
    AbstractErrorHandler.handle();
}

Ответ 5

Я считаю, что это связано с тем, что static является классом, и вы говорите компилятору неявно использовать AbstractErrorHandler, используя T extends AbstractErrorHandler.

Время выполнения будет принимать наивысший уровень класса, поскольку стирание стилей происходит во время выполнения.

Реализация m использует только T, которая является AbstractErrorHandler, несмотря на то, что вы объявили ее конкретным типом в основном методе, который не входит в объем метода m.

Ответ 6

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

см. более подробную информацию: https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html

Ответ 7

Причина в том, что вы используете generics и java static methods, которые не скрыты. Во время компиляции единственной известной информацией является класс AbstractErrorHandler (generics работает во время компиляции в java, байт-код с информацией генериков отсутствует), а метод называется одним из класса. Если вы измените форму дескриптора метода static на "экземпляр", реализация называется "правильной" (потому что метод переопределен не скрыт), как в приведенном ниже примере.

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        A<AbstractErrorHandler> a = new A<AbstractErrorHandler>();
        a.m(new ConcreteErrorHandler()); //Exception here!
    }

    public  static class AbstractErrorHandler {
        public  void handle(){ 
            throw new UnsupportedOperationException("Not implemented");
        }
    }

    public static class ConcreteErrorHandler extends AbstractErrorHandler{
        public  void handle(){ 
            System.out.println("Concrete handler");
        }
    }

    public static class A<T extends AbstractErrorHandler>{
        public void m(T t){
            t.handle();
        }
    }
}