Динамическое связывание Java

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

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, я получил что-то совершенно другое:

Кудрявый 1
Вьющиеся 2
Ларри 1
Ларри 1
Кудрявый 1
Кудрявый 1
Кудрявый 1
Вьющиеся 2
Кудрявый 3
Кудрявый 3
Кудрявый 1
Вьющиеся 2
Ларри 2
Ларри 2
Ларри 1

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

Спасибо

3 ответа

Решение

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

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

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

  • Ларри - stooge1 -> Кудрявый
  • Мое - stooge2 -> Ларри
  • Мо - stooge3 -> вьющиеся
  • Кудрявый - stooge4 -> Кудрявый
  • Ларри - stooge5 -> Ларри

  • 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.

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

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

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

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

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

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

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