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

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

У меня есть два класса:

public class ClassA {
    public void method(Number n) {
        System.out.println("ClassA: " + n + " " + n.getClass());
    }
}

и

public class ClassB extends ClassA {            
    public void method(Integer d) {
        System.out.println("ClassB: " + d + " " + d.getClass());
    }
}

Но когда я запускаю:

ClassA a = new ClassB(); 
a.method(3);

Я получаю:

ClassA: 3 class java.lang.Integer

Мой вопрос: почему не используется метод ClassB? a является экземпляром ClassB, а ClassB method() имеет параметр Integer...

4b9b3361

Ответ 1

Мой вопрос: почему не используется метод ClassB?

Не верно. Используемый метод - это метод ClassB, который он унаследовал от ClassA.


Я думаю, что основной причиной путаницы здесь является тот факт, что метод на самом деле не переопределен, вместо этого он перегружен. Хотя Integer является подтипом Number, поскольку параметр метода инвариантен в Java, метод public void method(Integer d) не переопределяет метод public void method(Number n). Таким образом, ClassB заканчивается наличием двух (перегруженных) методов.

Статическая привязка используется для перегруженных методов, а метод с наиболее конкретным типом параметра выбирается компилятором. Но в этом случае, почему компилятор выбирает public void method(Number n) вместо public void method(Integer d). Это из-за того, что ссылка, которую вы используете для вызова метода, имеет тип ClassA.

ClassA a = new ClassB(); //instance is of ClassB (runtime info)
a.method(3);             //but the reference of type ClassA (compiletime info)

Единственный метод, который имеет ClassA, имеет public void method(Number n), так что компилятор подбирает. Помните, что здесь ожидаемый тип аргумента Number, но фактический аргумент, целое число 3, передается автоматически с типом Integer. И причина, по которой он работает, состоит в том, что аргумент метода является ковариантным в Java.

Теперь я думаю, что понятно, почему он печатает

ClassA: 3 класс java.lang.Integer

Ответ 2

Ваша проблема связана с тем, что (как указано в официальных руководствах Java по наследованию):

В подклассе вы можете перегружать методы, унаследованные от суперкласса. Такие перегруженные методы не скрывают и не переопределяют методы суперкласса - это новые методы, уникальные для подкласса.

Для получения дополнительной информации обратитесь к официальным руководствам по Java. http://docs.oracle.com/javase/tutorial/java/IandI/override.html

Ответ 3

a имеет тип ClassA, поэтому методы класса B не будут отображаться экземпляру a, если он не объявлен как ClassB

ClassB a = new ClassB(); 

будет выдавать ожидаемый результат. Число - это супертип Integer. Так что независимо от того, что вы передадите, будет автобоксирован соответствующий подтип, и будет вызван метод в ClassA. Попробуйте пройти

a.method(3.0f) // Float
a.method(3.0) // Double

Ответ 4

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

Ответ 5

потому что номер 3 автоматически помещается в поле Integer.

перейдите по ссылке ниже:   http://www.javabeat.net/articles/print.php?article_id=31

Общее правило: аргументы неявно расширены для соответствия параметрам метода. Нельзя расширять с одного класса-оболочки на другой.

Ответ 6

Поскольку две операции имеют разные типы аргументов (параметров) (хотя они и являются подклассами), они считаются разными (в отличие от C) вы не переопределили первый метод со вторым. Вместо этого вы попали в класс B, который теперь имеет два метода

 public void method(Number n)  and
 public void method(Integer n)

по умолчанию, когда вы выполнили метод a.method(3) 3, к объекту Integer. Вы можете проверить это, вызвав

a.method((Number)3);      //this would call the second method/operation.

Вы также можете проверить это, используя отражение для итерации по методам класса B.

Ответ 7

  class ClassA
  {
     public void method( Number n )
     {
        System.out.println( "ClassA: " + n + " " + n.getClass() );
     }// void method( Number n )

  }// class ClassA

  public class ClassB
     extends
        ClassA
  {
     public void method( Integer d )
     {
        System.out.println( "ClassB: " + d + " " + d.getClass() );
     }// void method( Integer d )

     public static void main( String[] args )
     {
        ClassB b = new ClassB(); 
        ClassA a = b; 
        a.method( new Integer( 3 )); // 1. ClassA: 3 class java.lang.Integer
        b.method( new Integer( 4 )); // 2. ClassB: 4 class java.lang.Integer
        b.method( new Float( 5.6 )); // 3. ClassA: 5.6 class java.lang.Float
     }// void main( String[] args )

  }// class ClassB
  • Поскольку эти два метода НЕ перегружены, а экземпляр класса a, отправка от A до B не происходит.
  • B имеет наилучший метод совпадения, затем он выбирает
  • B не может обрабатывать параметр типа Float, поэтому выбран метод A

Ответ 8

Чтобы очистить, я добавил, show() метод как в classA, так и classB.

public void show() {
        System.out.println(getClass());
    }

Я называю это,

    // Case 1       
    ClassA a = new ClassB();
    a.method(3);// ClassA: 3 class java.lang.Integer
    a.show(); // class ClassB

    // Case 2       
    ClassB b = new ClassB();
    b.method(3);// ClassB: 3 class java.lang.Integer
    b.show(); // class ClassB

Здесь метод (число n) и метод (целое число d) имеют разные подписи. Это не главное. Это перегрузка.

Но метод show() переопределяет метод.

В случае 1, Доступны только объекты класса A с объектом a. a - тип classA, методы в классе B не видны. Вот почему вы вызываете метод classA. Но для метода show(), поскольку он переопределен, вызывается метод show() класса B.

В случае 2, Оба метода класса A и B доступны с объектом b, поскольку ClassB расширяет ClassA.

Ответ 9

У вас был следующий код

Class A a = new ClassB(); 
a.method(3);

Но представьте, что у вас есть метод, где "a" и "3" передаются вам как параметр, и вы все равно выполняете тот же код

public void foo(A a, Number n)
{
    a.method(n);
}

Компилятор не знает, собираетесь ли вы передавать класс A или класс B (или число или целое число). Он должен все же разрешить метод, чтобы он мог выполнять проверку типов для возвращаемого значения из метода a.method

Ответ 10

Я сделал несколько R & D по этой проблеме и придумал решение, чтобы удалить ваше замешательство. Надеюсь, это поможет вам понять.

Посмотрите на код ниже:

class A {
    public void func(Number obj){
        System.out.println("In a func");
    }
    public void func(Integer obj){
        System.out.println("In b func");        
    }
}

class B extends A {
}

public class X {

    public static void main(String s[]){
        B b = new B();
        b.func(3);

        A a = new B();
        a.func(3);
    }
}

Если вы запустите этот код, вы получите результат:
" В b func"
" В b func".

В этом случае здесь 4 метода:

  • класс A имеет как перегруженные методы: func (Number) [say method 1] и func (Integer) [say method 2]
  • класс B также имеет 2 метода из-за наследования. Таким образом, он имеет func (Number) [say method 3] и func (Integer) [say method 4]

Теперь, когда вы вызываете b.func(3) по ссылке B, он увидит "метод 3" и "метод 4", которые имеют параметр с наиболее подходящим производным классом. Здесь оба класса Number и Integer подходят для аргумента 3, но Integer выводится из Number, поэтому будет вызван func (Integer) [метод 3]. Следовательно, выход "In b func"

Второй вывод также "В b-методе", из-за той же логики. Прежде всего помните, вы не можете вызвать какой-либо метод в описании класса A, который не имеет класса A. Таким образом, вы можете вызывать только эти методы по ссылке класса A, которые у него есть. Независимо от того, экземпляр имеет класс A или его экземпляр подкласса.

Вам нужно понять это в двух терминах, компиляции и компоновке и выполнении.

Теперь класс A имеет оба метода, поэтому, когда компилятор ищет a.func(3) по ссылке класса A. Компилятор будет изучать "методы 1" и "метод 2" класса A и связывать сигнатуру метода, которая имеют аргументы с наиболее подходящим производным классом. Таким образом, "func (Integer)".

Теперь во время выполнения будет выполняться функция func (Integer), она называется классом B, потому что экземпляр имеет класс B. (Во время выполнения метод выполняется из класса, экземпляр которого вызывает метод). Так называется метод 4. Следовательно, выход.

Я уверен, у вас будет путаница, почему метод 2 не вызывается и вызывается метод 4.

Если вы запустите код ниже:

class A {
    public void func(Number obj){
        System.out.println("In a func");
    }
    public void func(Integer obj){
        System.out.println("In b func");        
    }
}

class B extends A {
    public void func(Number obj){
        System.out.println("In a func of class B");
    }
    public void func(Integer obj){
        System.out.println("In b func of class B");     
    }
}

public class X {

    public static void main(String s[]){
        B b = new B();
        b.func(3);

        A a = new B();
        a.func(3);
    }
}

Выход будет:
В b func класса B
В b func класса B

Теперь вы можете понять этот код с приведенным выше объяснением. Либо мы назвали fun (3) ссылкой на класс A или класс B. Каждый раз, когда вызывается метод класса B (метод 4). Потому что экземпляр имеет класс B. Но если класс A не будет иметь (метод 2). Метод 4 не будет вызываться на "a.func(3)"

Давайте посмотрим ниже кода:

class A {
    public void func(Number obj){
        System.out.println("In a func");
    }
}

class B extends A {
    public void func(Integer obj){
        System.out.println("In b func");        
    }
}
public class X {

    public static void main(String s[]){
        B b = new B();
        b.func(3);

        A a = new B();
        a.func(3);
    }
}

Вывод этой программы:
В b func
В func

Теперь у вас может быть путаница, почему это другой вывод?

Помните, что здесь не 4 метода. Вот только 3 метода:

  • func (Number) в классе A
  • func (Number) в классе B, унаследованный от класса A
  • func (целое число) в классе B

Теперь, если вы вызываете a.fun(3), класс A не имеет func (Integer), и вы не можете вызывать метод по ссылке класса A, у которого нет класса. Поэтому компилятор не будет связывать func (Integer), потому что такого метода нет в классе A. Но есть еще один метод func (Number), который можно вызывать с тем же кодом a.func(3) Концепция автобоксинга java.

Поэтому, когда вызываемый вызов a.func(3), он в основном вызывает func (Number). Теперь, поскольку экземпляр имеет класс B, вызывается метод func (Number) класса B. Следовательно, выход "В func"

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

Наслаждайтесь кодированием!

Ответ 11

При перегрузке решение метода всегда заботится о компиляторе на основе ссылочного типа. Итак, при перегрузке объекта времени выполнения [new ClassB()] не играет никакой роли. Поэтому в вашем случае был выполнен метод ClassA.

ClassA a = new ClassB(); 
a.method(3);