Почему Java обеспечивает совместимость возвращаемых типов для переопределенных статических методов? - PullRequest
30 голосов
/ 25 февраля 2012

За этот ответ и этот ответ , статические методы Java не являются виртуальными и не могут быть переопределены.Таким образом, интуитивно это должно работать (даже если в 99% случаев это опасное программирование):

class Foo
{
    public static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    public static Number frob() {
        return 123;
    }
}

Однако на практике это дает вам:

Foo.java:10: frob() in Bar cannot override frob() in Foo; attempting to use incompatible return type
found   : java.lang.Number
required: java.lang.String
    public static Number frob() {
                         ^

Наивно это кажетсякак Foo.frob() и Bar.frob() не должны иметь ничего общего друг с другом;все же Java настаивает на том, что они делают.Почему?

(Примечание: я не хочу слышать, почему было бы плохой идеей кодировать таким образом, я хочу услышать, что это за Java и / или дизайн JVM, который делает это ограничение необходимым.)


Обновлено для добавления: Для тех, кто думает, что компилятор может запутаться, вызывая статические методы в экземплярах, если вы разрешите это: это не так.Это уже нужно выяснить в случае, когда сигнатуры методов совместимы с :

class Foo
{
    static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    static String frob() {
        return "Bar";
    }
}

class Qux {
    public static void main(String[] args) {
        Foo f = new Foo();
        Foo b = new Bar();
        Bar b2 = new Bar();

        System.out.println(f.frob());
        System.out.println(b.frob());
        System.out.println(b2.frob());
    }
}

дает вам:

Foo
Foo
Bar

Вопрос в том, чтоконкретная причина, по которой он не мог так легко (в случае несовместимых подписей) получить вас:

Foo
Foo
123

Ответы [ 4 ]

7 голосов
/ 25 февраля 2012

Рассмотрим следующее:

public class Foo {
  static class A {
    public static void doThing() {
      System.out.println("the thing");
    }
  }

  static class B extends A {

  }

  static class C extends B {
    public static void doThing() {
      System.out.println("other thing");
    }
  }

  public static void main(String[] args) {
    A.doThing();
    B.doThing();
    C.doThing();
  }
}

Запустите это! Компилирует и печатает

the thing
the thing
other thing

Статические методы своего рода наследуют - в том смысле, что B.doThing переводится в вызов A.doThing - и могут быть переопределены.

Похоже, это был в основном призыв к решению JLS. Тем не менее, наиболее конкретным способом решения этой проблемы в JLS является section 8.2 , в котором просто не говорится, что статические методы не наследуются.

3 голосов
/ 25 февраля 2012

JLS 8.4.2 Подпись метода , кратко:

Два метода имеют одинаковую подпись, если они имеют одинаковые имена и типы аргументов.

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

2 голосов
/ 25 февраля 2012

Это связано с тем, что в Java определенный метод вызывается на основе типа времени выполнения объекта, а не типа его времени компиляции. Однако статические методы являются методами класса, и, следовательно, доступ к ним всегда разрешается во время компиляции только с использованием информации о типе времени компиляции. То есть, что случилось бы, если бы вы могли скомпилировать выше и использовать код, подобный этому

Foo bar = new Bar();
bar.frob();
1 голос
/ 25 февраля 2012

Что ж, JVM, вероятно, можно было бы сделать, чтобы позволить это, но давайте поговорим о том, почему это довольно плохая идея с точки зрения компилятора.что-то, что является простой проблемой, которую нужно решить во время компиляции.Очевидно, это хорошо известно во время выполнения.Если у меня есть переменная bar типа Bar, и я вызываю s = bar.frob (), компилятору потребуется перепроектировать, какой тип bar должен видеть, является ли возвращаемое значение приемлемым.Если определение типа во время компиляции является очень сложной задачей, это делает компилятор в лучшем случае неэффективным.В худшем случае ответ неправильный, и вы получаете ошибки времени выполнения, которые должны были быть обнаружены во время компиляции.

...