Динамическое связывание Java - PullRequest
2 голосов
/ 16 марта 2009

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

class Moe {
    public void print(Moe p) {
        System.out.println("Moe 1\n");
    }
}
class Larry extends Moe {
    public void print(Moe p) {
        System.out.println("Larry 1\n");
    }
    public void print(Larry l) {
        System.out.println("Larry 2\n");
    }
}
class Curly extends Larry {
    public void print(Moe p) {
        System.out.println("Curly 1\n");
    }
    public void print(Larry l) {
        System.out.println("Curly 2\n");
    }
    public void print(Curly b) {
        System.out.println("Curly 3\n");
    }
}
public class Overloading_Final_Exam {
    public static void main (String [] args) {
        Larry stooge1 = new Curly();
        Moe stooge2 = new Larry();
        Moe stooge3 = new Curly();
        Curly stooge4 = new Curly();
        Larry stooge5 = new Larry();
        stooge1.print(new Moe()); 
        ((Curly)stooge1).print(new Larry()); 
        ((Larry)stooge2).print(new Moe()); 
        stooge2.print(new Curly()); 
        stooge3.print(new Curly()); 
        stooge3.print(new Moe()); 
        stooge3.print(new Larry()); 
        ((Curly)stooge3).print(new Larry()); 
        ((Curly)stooge3).print(new Curly()); 
        stooge4.print(new Curly()); 
        stooge4.print(new Moe()); 
        stooge4.print(new Larry()); 
        stooge5.print(new Curly()); 
        stooge5.print(new Larry()); 
        stooge5.print(new Moe()); 
    }
}

Я имел в виду мои идеи, но потом, когда я запустил Java, я получил что-то совершенно другое:

Curly 1
Curly 2
Larry 1
Larry 1
Curly 1
Curly 1
Curly 1
Curly 2
Curly 3
Curly 3
Curly 1
Curly 2
Larry 2
Larry 2
Larry 1

Первые несколько в порядке, но потом я действительно не понимаю. У кого-нибудь есть хорошее объяснение этой проблемы?

Спасибо

Ответы [ 3 ]

5 голосов
/ 16 марта 2009

Я бы начал рисовать картинку ...

Moe - print(Moe)
 |
Larry - print(Moe), print(Larry)
 |
Curly - print(Moe), print(Larry), print(Curly)

Тогда я бы отслеживал переменные:

  • Ларри - stooge1 -> Кудрявый
  • Мое - stooge2 -> Ларри
  • Мое - stooge3 -> Вьющиеся
  • Вьющиеся - stooge4 -> Вьющиеся
  • Ларри - марионетка5 -> Ларри

  • stooge1.print(new Moe())

    • stooge1 -> Curly, поэтому вызывает Curly.print (Moe)
  • ((Curly)stooge1).print(new Larry());

    • stooge1 -> Curly, поэтому вызывает Curly.print (new Larry ())
  • ((Larry)stooge2).print(new Moe());

    • stooge2 -> Ларри так называет Larry.print (new Moe ());
  • stooge2.print(new Curly());
    Хорошо, здесь немного сложнее (извините, я остановился здесь раньше)

    • stooge2 объявлен Мо. Поэтому, когда компилятор ищет, что вызывать, он вызывает метод print (Moe). Затем во время выполнения он знает, что stooge2 - это Larry, поэтому он вызывает метод Larry.print (Moe).

и т.д ...

Дайте мне знать, что если вам не удастся выполнить весь этот шаг, то все получится.

(обновлено для уточнения следующего)

Итак, общее правило:

  • компилятор просматривает тип переменной, чтобы решить, какой метод вызывать.
  • среда выполнения смотрит на реальный класс, на который указывает переменная, чтобы решить, откуда взять метод.

Итак, когда у вас есть:

Moe stooge2 = new Larry();
stooge2.print(new Moe());

компилятор говорит:

  • можно ли назначить Ларри на stooge2? (да, так как Ларри является подклассом Мо)
  • Есть ли у Мо метод печати (Мо)? (Да)

среда выполнения говорит:

  • Я должен вызвать метод print (Moe) для этого объекта здесь ... stooge2
  • Stooge2 - это точка на Ларри.
  • Я вызову метод print (Moe) в классе Larry.

Как только вы все это поработаете, попробуйте избавиться от некоторых методов и посмотрите, как это меняет дело.

2 голосов
/ 16 марта 2009

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

Общее правило, упомянутое TofuBeer, верно только в случае динамического связывания. При статическом связывании решения принимаются только во время компиляции.

Ваш пример смешивает динамическое связывание (когда методы переопределены) и статическое связывание (когда методы перегружены).

Посмотрите на этот вопрос для более подробной информации.

0 голосов
/ 16 марта 2009

Подсказка - игнорировать значение слева при взгляде на объекты. Вместо этого посмотрите на значение right во время объявления, это фактическое значение объекта.

...