Java не наследует методы доступа? - PullRequest
0 голосов
/ 12 января 2009

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

Ответы [ 6 ]

13 голосов
/ 12 января 2009

Проблема в том, что ваша дубликат декларации членов dee и dum из Foo в Bar скрывает элементы из Foo. Bar имеет своих членов; те из Foo никогда не будут использоваться Bar. Вы имеете в виду что-то вроде

public class Bar extends Foo {
  {
    dee = "DEE";
    dum = "DUM";
  }
}
9 голосов
/ 12 января 2009

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

public class Bar extends Foo {
    public Bar() {
        dee = "DEE";
        dum = "DUM";
    }
}

должно работать лучше.

5 голосов
/ 12 января 2009

Когда вы вызываете mybar.getDee(), вы вызываете метод, определенный в базовом классе Foo. (Этот метод был унаследован от Bar. В противном случае вы бы не смогли вызвать его для переменной экземпляра Bar.) Этот метод возвращает значение dee field, но это поле dee, определенное в классе Foo - классе, в котором был определен сам метод. Компилятор разрешил ссылку на поле во время компиляции метода в классе Foo.

Некоторые другие ответы использовали слово override , чтобы определить, что вы сделали, объявив поле с именем dee в Bar, но это не то, что произошло. Вы не можете переопределить поле, потому что поля не являются виртуальными. Возможно, вы так и думали. Если бы существовала такая вещь, как «виртуальное поле», я бы тоже мог ожидать, что getDee() вернет версию поля класса выполнения (в Bar) вместо той, которая находилась в области действия в то время метод был скомпилирован (Foo). Но это просто не то, как работает Java (или C #, или C ++, или Delphi, или любой другой из известных мне языков). На каком языке вы привыкли, где это работало бы?

2 голосов
/ 12 января 2009

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

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

1 голос
/ 13 января 2009

Что касается вашего второго «вопроса» ...

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

...

"... System.out.println (baz.getDee ()); // 'DEE', но ожидалось бы '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

1 голос
/ 12 января 2009

Accessors не проблема, это поля. Средства доступа относятся к Foo.this.dee, а не к Bar.this.dee, которые являются отдельными.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...