Подтвердить что ты не робот

Вызов перегруженных унаследованных методов с использованием ссылки суперкласса

Я не понимаю этого поведения Java. У меня есть два класса:

class C1 {
    public void m1(double num) {
        System.out.println("Inside C1.m1(): " + num);
    }
}

class C2 extends C1 {
    public void m1(int num) {
        System.out.println("Inside C2.m1(): " + num);
    }
}

И это моя главная:

public class Main {

    public static void main(String[] args) {
        C1 c = new C2();
        c.m1(10);
    }
}

И результат:

Inside C1.m1(): 10.0

Когда я ожидал:

Inside C2.m1(): 10

Также, когда я пытаюсь выполнить синтаксис кода, я нашел это:

Enter image description here

Где находится другой m1 класса C2?

Я также проверяю байт-код моего Main.class, и я видел это:

Compiled from "Main.java"
public class com.company.Main {
  public com.company.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/company/C2
       3: dup
       4: invokespecial #3                  // Method com/company/C2."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc2_w        #4                  // double 10.0d
      12: invokevirtual #6                  // Method com/company/C1.m1:(D)V
      15: return
}

Байт-код говорит мне, что он будет вызывать C1.m1 (D) V (строка 12).

Почему метод C1? Я пытаюсь понять это поведение.

4b9b3361

Ответ 1

У ваших двух методов с именем m1 нет такой же подписи; один в суперклассе принимает a double, а один в подклассе принимает int. Это означает, что компилятор выберет сигнатуру метода для вызова на основе типа времени компиляции переменной C1 и вызовет m1(double). Поскольку во время выполнения класс C2 не имеет переопределяющей версии m1(double), вызывается версия из C1.

Правило заключается в том, что сигнатуры метода вычисляются во время компиляции на основе типов времени компиляции; вызовы методов отправляются во время выполнения на основе совпадающих подписей.

Ответ 2

Это из-за параметров. Метод, который вы вызываете, - это метод с двойным параметром. m1 внутри C2 не переопределяет это, вместо этого он перематывает его.

Если вы хотите вызвать m1 в C2, вам нужно указать ссылку, чтобы компилятор принял то, что вы делаете.

Ответ 3

Причина, по которой вы видите вывод Inside C1.m1(): 10.0, а не Inside C1.m1(): 10 или Inside C2.m1(): 10.0, заключается в следующем:

  • Вы не переопределяете метод m1 в C2. Вы перегружаете метод m1(doube), который вы унаследовали от C1 до m1(int).
  • Класс C2 теперь имеет два метода m1. Тот, который inherited из C1 и имеет подпись m1(double) и ту, которая перегружена в C2 и имеет подпись m1(int)
  • Когда компилятор видит вызов c.m1(10), он разрешает этот вызов на основе ссылочного типа. Поскольку ссылочный тип C1, компилятор собирается разрешить этот вызов m1(double) в C1.
  • Во время выполнения JVM собирается разрешить вызов m1(double) в C2, который является методом, унаследованным от C1. (Как объясняется в пункте 2)

Существует два способа вызова метода m1(int):

((C2)c).m1(10);

ИЛИ

C2 c = new C2(); c.m1(10);

Ответ 4

Java выполняет метод отправки по статическим типам, а ваша переменная c имеет тип C1, поэтому m1(int) не отображается, а ваш 10 добавляется к double.

Ответ 5

Подписи методов для обоих методов различны.

public void m1(double num) 
public void m1(int num)

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

    C1 c = new C2();
    c.m1(10);

во время компиляции он будет ссылаться на тип C1, у которого есть метод public void m1(double num), который совместим с 10 [int в расширенном, чтобы удвоить]. Таким образом, int удваивается, и вызывается соответствующий метод (который также вы видите в байт-кодах).

Ответ 6

Если вы вызовете c.m1 (10.0), он вызовет метод предка, как вы ожидали вначале.

Вы выполняете перегрузку метода в своем примере (то есть добавляете больше методов с тем же именем и другой подписью) вместо переопределения метода (что изменяет реализацию метода предка у потомка путем повторного использования его с той же сигнатурой, Одноименное имя AKA и тот же тип результата и аргументы метода - имена аргументов не должны иметь значения).

Ответ 7

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

C1 c = new C2();

to

C2 c = new C2();

Ответ 8

Вы не можете видеть методы C2, потому что переменная экземпляра объявлена ​​как C1, а также потому, что у них нет одинаковой сигнатуры. У вас есть двойной параметр в одном методе, а во втором - тип int, который делает их для JVM совершенно разными методами (поэтому наследование не будет работать здесь).

Итак, если у вас есть тип int в методе C1, тогда вам нужно также иметь тип int в методе C2, тогда JVM будет запускать метод из C2, как вы хотели.

Также вы можете использовать переменную типа C2, тогда вы сможете получить доступ к методам C2.

Ответ 9

Так как C1.m1(double num) является общедоступным методом, он унаследовал C2. Таким образом, ваш C2 также имеет метод m1(double num), и поэтому его называют. Из main() вы на самом деле вызывали C2.m1(double num).

Примечание: теперь в классе C2 у вас есть два перегруженных метода - m1(int num) и m1(double num). И C2.m1(int num) - это другой метод из C2.m1(double num).

Ответ 10

Java выбирает наиболее подходящий тип. В этом случае m1 (int) неприменимо.  Акцент на ссылке класса, которые содержат объект того же класса (c1) или объект любых подклассов класса (c2), и имя метода и список параметров.

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

Итак, во время (вызова) вызова метода есть много вещей.

Да для случая ур ваш основной класс должен быть таким

        public static void main(String[] args) {
            C1 c = new C2();
            c.m1(10);
            ((C2) c).m1(10);
    //or
            C2 cobj = new C2();
            cobj.m1(10);
        }


**OutPut**
Inside C1.m1(): 10.0
Inside C2.m1(): 10
Inside C2.m1(): 10