Java не наследует методы доступа?

Учитывая класс «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, но почему разница в объявлении? Мне кажется "неопределенным".

И начинается безумие кормления ...

Michael Myers 12.01.2009 22:55

Еще раз классический случай: «Я понятия не имею, как на самом деле работает Икс, поэтому Икс должно быть совершенно неверным».

Bombe 12.01.2009 23:29

«В школах Java не учат Java. Лента в одиннадцать».

user3458 13.01.2009 01:48
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
1 459
6

Ответы 6

Сами методы доступа (например, getDee()) наследуются, а переменные экземпляра - нет.

Если бы переменные экземпляра могли быть переопределены в подклассах (как вы пытаетесь это сделать здесь), это вызвало бы гораздо больше проблем, чем могло бы исправить.

вот что я пытаюсь понять. Какие проблемы это вызовет?

user54311 12.01.2009 23:02

а что насчет статических переменных?

user54311 12.01.2009 23:04

Хм, мне приходит в голову, что ответ, который я собирался дать, был где-то между «тебе не следует этого делать» и «зачем тебе это нужно?» Так что простите меня, пока я иду искать ссылки. :)

Michael Myers 12.01.2009 23:14

Вы скрываете унаследованные переменные с переменными, определенными в подклассе.

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";
  }
}

Наоборот. Его проблема в том, что он не переопределяет их - он прячется их.

Draemon 12.01.2009 23:05

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

user54311 12.01.2009 23:20

В качестве следующего трюка я попытаюсь выучить испанский, написав эссе на тему «Почему единороги круче грифонов» в MS Word, и позволю Clippy исправить мою орфографию и грамматику.

user54311 12.01.2009 23:27

Проблема не в аксессуарах, а в полях. Аксессоры относятся к 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?

Другие вопросы по теме