Почему этот метод не выбран на основе типа времени выполнения его объекта? - PullRequest
1 голос
/ 17 декабря 2009

Учтите это:

class A  {
    int x =5;
}

class B extends A{
        int x =6;
    }
public class CovariantTest {

    public A getObject() {
        return new A();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        CovariantTest c1 = new SubCovariantTest();
        System.out.println(c1.getObject().x);
    }

}

class SubCovariantTest extends CovariantTest {
    public B getObject(){
        return new B();
    }
}

Насколько я знаю, JVM выбирает метод, основанный на истинном типе своего объекта. Здесь истинным типом является SubCovariantTest, который определил переопределяющий метод getObject.

Программа печатает 5 вместо 6. Почему?

Ответы [ 3 ]

10 голосов
/ 17 декабря 2009

Метод действительно выбирается типом среды выполнения объекта. Что не выбрано типом среды выполнения, так это целое число поле x. Для объекта B существует две копии x, одна для A.x и одна для B.x. Вы статически выбираете поле из A класса, так как тип времени компиляции объекта, возвращаемого getObject, равен A. Этот факт можно проверить, добавив метод к A и B:

class A  {
    public String print() {
        return "A";
    }
}

class B extends A {
    public String print() {
        return "B";
    }
}

и изменение тестового выражения на:

System.out.println(c1.getObject().print());
1 голос
/ 17 декабря 2009

Когда поля в супер и подклассах имеют одинаковые имена, это называется «скрытием». Помимо проблем, упомянутых в вопросе и ответе, существуют и другие аспекты, которые могут вызвать тонкие проблемы:

С http://java.sun.com/docs/books/tutorial/java/IandI/hidevariables.html

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

Некоторые компиляторы предупреждают о скрытии переменных

1 голос
/ 17 декабря 2009

Если я не ошибаюсь, методы в Java являются виртуальными по умолчанию, поэтому вы корректно переопределяете метод. Однако поля (например, 'x') не являются виртуальными и не могут быть переопределены. Когда вы объявляете «int x» в B, вы фактически создаете совершенно новую переменную.

Полиморфизм не вступает в силу для полей, поэтому при попытке получить x для объекта, приведенного к типу A, вы получите 5, если объект приведен к типу B, вы получите 6.

...