Наследование и приведение в Java

У меня вопрос о наследовании и приведении типов в Java. У меня есть следующие два примера классов и тестовый класс, и я задаю свой вопрос после классов:

public class Automobile {
    public int var;

    public Automobile () {
        var = 1;
    }

    public String toString () {
        return "AUTOMOBILE: " + var;
    }
}


public class Porsche extends Automobile {
    public int var;

    public Porsche () {
        var = 2;
    }

    public String toString () {
        return "PORSCHE: " + var;
    }
}

public class Test {
    public static void main (String [] args) {
        Porsche p = new Porsche();
        Automobile a = new Automobile();

        System.out.println ("(Automobile) p = " + (Automobile)p);

        System.out.println ("(Automobile) p.var = " + ((Automobile)p).var); 
    }
}

Результат:

(Automobile) p = PORSCHE: 2
(Automobile) p.var = 1

Я не понимаю, почему во втором утверждении у нас есть 1. Разве это не должно быть 2? Потому что после того, как я привел p к Automobile в первом выражении, я все еще получаю PORSCHE: 2 как представление p - я понимаю это следующим образом:

тем не менее, я преобразовал p в автомобиль, p сохраняет свою "первоначальную природу" - p является объектом класса Porsche, но поскольку Porsche расширяет автомобиль, мы могли бы сказать, что p также является автомобилем. И поэтому, когда мы явно приводим его к Automobile, он продолжает использовать свои методы — в нашем случае метод toString(), определенный в Porsche.

С другой стороны, если то, что я пишу, верно, то второй оператор печати должен давать 2, а не 1.

Теперь это кажется мне противоречием, но поскольку это работает на Java, кажется, что я не понимаю, что происходит во время приведения и процесса наследования.


person user42155    schedule 30.01.2009    source источник


Ответы (5)


Я считаю, что вы не можете переопределить переменные в Java. Подкласс фактически «скрывает» переменную var.

При приведении к Automobile вы получаете версию суперкласса переменной var. Однако метод toString() по-прежнему просматривает версию экземпляра переменной var.

Если вы удалили public int var из подкласса Porsche, он должен работать как положено.

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

person Community    schedule 30.01.2009

Затенение переменной var.

Ваша переменная Porche.var отличается от переменной Automobile.var.

Метод Porsche.toString() использует версию Porsche.var, тогда как приведение указывает компилятору использовать версию Automobile.

person jamesh    schedule 30.01.2009

Доступ к полям не является полиморфным — он зависит только от типа выражения во время компиляции.

Выражение ((Automobile)p) имеет тип времени компиляции Automobile, поэтому используются его поля.

person Pete Kirkham    schedule 30.01.2009

Объявление переменной "var" в обоих классах фактически создает два поля в памяти.

Экземпляр "Porsche" будет иметь два поля "var" в памяти, одно из класса "Porsche" и одно из суперкласса "Automobile". Поле «var» суперкласса скрыто, когда вы ссылаетесь на «var» из известного экземпляра «Porsche». Если вы приводите этот экземпляр к «Автомобилю» и ссылаетесь на поле «var», вы ссылаетесь на поле var «Автомобиля».

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

Для получения ожидаемого результата вообще не нужно объявлять поле "var" в классе "Porsche".

person Thanasis Ioannidis    schedule 26.03.2013

Однако :

Если у вас есть метод, который возвращает объект родительского класса A, он может возвращать любой тип объекта класса, который его расширяет (пример factory). Теперь, если этот метод возвращает объект подкласса B, и если вы не приведете его к типу B, все его атрибуты будут родительскими атрибутами. Если вы приведете его к B, то его атрибуты будут атрибутами A и B.

class A {
int a1,a2;
public A(){}


  public static A getInstance () {
  return new B() ;
  }
}
class B {
int b1,b2;
  public B() {}

}
 class Test{

 public static void main (String [] args) {

  A theA = A.getInstance() ; //here theA has only a1,a2 as attributes
  B theB = (B)theA    // here theB has b1,a2 and a1,a2 as attributes 
  }


}
person Java Main    schedule 26.12.2015