Учитывая класс «Bar», который расширяет класс «Foo», реализующий интерфейс «DeeDum»
public interface DeeDum {
public String getDee();
public String getDum();
}
public class Foo implements DeeDum {
public String dee = "D";
public String dum;
public String getDee() { return dee; }
public String getDum() { return dum; }
}
public class Bar extends Foo {
public String dee = "DEE";
public String dum = "DUM";
}
Почему это не работает?
public static Bar mybar = new Bar();
Assert.assertEquals("DEE", mybar.getDee());
Assert.assertEquals("DUM", mybar.getDum());
Вместо этого я получаю "D" и ноль. Другими словами, Bar не наследует методы доступа от Foo и не может переопределять свойства. Каким-то образом вызов mybar.getDum () вызывает статический экземпляр класса Foo и возвращает статические свойства из родительского класса. Даже если свойства в дочернем классе переопределены! Означает ли это, что вы не можете наследовать методы или свойства любой?
Я не могу осмыслить это. Почему Java не может наследовать аксессоры (и почему они выбрали такую странную альтернативу?)
Или я просто что-то не так делаю?
На самом деле, я все еще вижу что-то странное и недетерминированное. Если у вас есть другой класс Bar, который расширяет Foo и устанавливает унаследованные методы доступа в блоке инициализации
Хотя вы можете установить родительское свойство в приведенном выше блоке, на самом деле оно не создает копию для дочернего класса.
Кажется, это недетерминированная инициализация для нескольких классов.
Итак, если у вас есть Bar и Baz, которые расширяют foo и имеют блок инициализации, похоже, что оба наследуют значение, установленное Bar.
public class Bar extends Foo {
{
dee = "dee";
dum = "dum";
}
}
public class Baz extends Foo {
{
dee = "DEE";
dum = "DUM";
}
}
public static Bar bar = new Bar();
public static Baz baz = new Baz();
System.out.println("mybaz: " + mybaz.getDee() + mybaz.getDum()); // DEEDUM
System.out.println("mybar: " + mybar.getDee() + mybar.getDum()); // DEEDUM
но если они созданы в другом порядке, я получаю:
public static Baz baz = new Baz();
public static Bar bar = new Bar();
System.out.println("mybaz: " + mybaz.getDee() + mybaz.getDum()); // deedum
System.out.println("mybar: " + mybar.getDee() + mybar.getDum()); // deedum
И все выглядит иначе, если в базовом классе Foo установлено значение по умолчанию.
Думаю, теперь я понимаю, что блок инициализации в Bar и Baz на самом деле устанавливает Foo :: dee и Foo :: dum, но почему разница в объявлении? Мне кажется "неопределенным".
Еще раз классический случай: «Я понятия не имею, как на самом деле работает Икс, поэтому Икс должно быть совершенно неверным».
«В школах Java не учат Java. Лента в одиннадцать».




Сами методы доступа (например, getDee()) наследуются, а переменные экземпляра - нет.
Если бы переменные экземпляра могли быть переопределены в подклассах (как вы пытаетесь это сделать здесь), это вызвало бы гораздо больше проблем, чем могло бы исправить.
вот что я пытаюсь понять. Какие проблемы это вызовет?
а что насчет статических переменных?
Хм, мне приходит в голову, что ответ, который я собирался дать, был где-то между «тебе не следует этого делать» и «зачем тебе это нужно?» Так что простите меня, пока я иду искать ссылки. :)
Вы скрываете унаследованные переменные с переменными, определенными в подклассе.
public class Bar extends Foo {
public Bar() {
dee = "DEE";
dum = "DUM";
}
}
должно работать лучше.
Проблема в том, что ваше дублированное объявление членов dee и dum из Foo в Bar скрывает таковые из Foo. Bar имеет своих членов собственный; те из Foo никогда не будут использоваться Bar. Вы имеете в виду что-то вроде
public class Bar extends Foo {
{
dee = "DEE";
dum = "DUM";
}
}
Наоборот. Его проблема в том, что он не переопределяет их - он прячется их.
да, плохой пример. Я пытался написать упрощенный пример для «компилятора Eclipse» - избавиться от всех значков в желобе и считать его правильным.
В качестве следующего трюка я попытаюсь выучить испанский, написав эссе на тему «Почему единороги круче грифонов» в MS Word, и позволю Clippy исправить мою орфографию и грамматику.
Проблема не в аксессуарах, а в полях. Аксессоры относятся к Foo.this.dee, а не к Bar.this.dee, которые являются отдельными.
Когда вы вызываете mybar.getDee(), вы вызываете метод, определенный в базовом классе Foo. (Этот метод было унаследован от Bar. В противном случае вам не было бы разрешено вызывать его в переменной экземпляра Bar.) Этот метод возвращает значение поля dee, но это поле dee, определенное в Foo. class - класс, в котором был определен сам метод. Компилятор разрешил ссылку на поле во время компиляции метода в классе Foo.
В некоторых других ответах использовалось слово отвергать для определения того, что вы сделали, объявив поле с именем dee в Bar, но этого не произошло. Вы не можете переопределить поле, потому что поля не виртуальные. Хотя, возможно, вы так и думали. Если бы существовала такая вещь, как «виртуальное поле», я тоже мог бы ожидать, что getDee() вернет версию поля класса времени выполнения (ту, что в Bar) вместо той, которая была в области видимости во время компиляции метода. (Foo's). Но это просто не так, как работает Java (или C#, или C++, или Delphi, или любой другой язык, о котором я знаю). На каком языке вы привыкли, чтобы это сработало?
Что касается вашего второго «вопроса» ...
Actually, I'm seeing something weird and undeterministic still...
...
"...System.out.println(baz.getDee()); // 'DEE' but would expect 'dee'"
Было бы полезно, если бы вы запустили программу перед публикацией вопроса, чтобы увидеть фактические результаты.
Вот что я получаю. Это "ди", как вы и ожидали.
Вы можете убедиться в этом сами, создав файлы, скомпилировав их и запустив, как показано ниже:
C:\oreyes\samples\java\dee>type DeeDum.java Foo.java Bar.java Baz.java Test.java
DeeDum.java
public interface DeeDum {
public String getDee();
public String getDum();
}
Foo.java
public class Foo implements DeeDum {
public String dee = "D";
public String dum;
public String getDee() { return dee; }
public String getDum() { return dum; }
}
Bar.java
public class Bar extends Foo {
{
dee = "DEE";
dum = "DUM";
}
}
Baz.java
public class Baz extends Foo {
{
dee = "dee";
dum = "dum";
}
}
Test.java
class Test {
public static Bar bar = new Bar();
public static Baz baz = new Baz();
public static void main( String [] args ) {
System.out.println(bar.getDee()); // 'DEE'
System.out.println(baz.getDee()); // 'DEE' but would expect 'dee'
}
}
C:\oreyes\samples\java\dee>javac *.java
C:\oreyes\samples\java\dee>java Test
DEE
dee
C:\oreyes\samples\java\dee>
PEBKAC?
И начинается безумие кормления ...