Что нужно использовать Upcasting в Java?

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

class Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Animal");
    }
}

class Dog extends Animal 
{ 
    public void callme()
    {
        System.out.println("In callme of Dog");
    }

    public void callme2()
    {
        System.out.println("In callme2 of Dog");
    }
}

public class UseAnimlas 
{
    public static void main (String [] args) 
    {
        Dog d = new Dog();      
        Animal a = (Animal)d;
        d.callme();
        a.callme();
        ((Dog) a).callme2();
    }
}

Вы можете рассмотреть этот пример для апскейтинга. Какая польза от апскейтинга здесь? И то и другое d а также a давая тот же вывод!

10 ответов

Решение

В большинстве ситуаций явный выброс не нужен и не имеет никакого эффекта.

В вашем примере явный upcast

    Animal a = (Animal)d;

можно заменить на это:

    Animal a = d;    // implicit upcast

Цель неявного преобразования (для типа объекта Java) состоит в том, чтобы "забыть" информацию о статическом типе, чтобы объект с определенным типом мог использоваться в ситуации, которая требует более общего типа. Это влияет на проверку типов во время компиляции, а не на поведение во время выполнения.

(Для примитивного типа восходящий результат приводит к преобразованию и может в некоторых случаях привести к потере точности; например, long -> float.)


Тем не менее, существуют ситуации, когда наличие явного отката изменяет значение выражения / выражения.

Одна из ситуаций, когда необходимо использовать апкастинг в Java, - это когда вы хотите принудительно использовать определенный метод переопределения; например, предположим, что у нас перегружены методы:

public void doIt(Object o)...
public void doIt(String s)...

Если у меня есть строка и я хочу вызвать первую перегрузку, а не вторую, я должен сделать это:

String arg = ...

doIt((Object) arg);

Связанный случай:

doIt((Object) null);

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

Вторая ситуация включает в себя случайные параметры:

public void doIt(Object... args)...

Object[] foo = ...

doIt(foo);  // passes foo as the argument array
doIt((Object) foo); // passes new Object[]{foo} as the argument array.

Третья ситуация - при выполнении операций с примитивными числовыми типами; например

int i1 = ...
int i2 = ...
long res = i1 + i2;           // 32 bit signed arithmetic ... might overflow
long res2 = ((long) i1) + i2; // 64 bit signed arithmetic ... won't overflow

Что нужно использовать Upcasting в Java?

Не уверен, что вы правильно поняли терминологию, но вот цитата, чтобы уточнить:

приведение к базовому типу
Выполнение приведения из производного класса в более общий базовый класс.

И вот один сценарий, где это действительно имеет значение:

class A {
}

class B extends A {
}

public class Test {

    static void method(A a) {
        System.out.println("Method A");
    }

    static void method(B b) {
        System.out.println("Method B");
    }

    public static void main(String[] args) {
        B b = new B();
        method(b);                      // "Method B"

        // upcasting a B into an A:
        method((A) b);                  // "Method A"
    }
}

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

Еще один пример, когда необходим подъем, - это ?: оператор, но я не помню сейчас. И иногда вам нужны upcasts в случае varargs-методов:

public void doSomething(Object...  bla) {}

Если мы хотим передать Object[] array как один параметр (не его объекты как отдельные), мы должны написать либо

doSomething((Object)array);

или же

doSomething(new Object[]{array});

Upcasting не имеет абсолютно никакого значения в вашем примере (на самом деле я не могу представить себе ни одного случая, где он есть), и его следует избегать, поскольку он только смущает разработчиков. Некоторые IDE (IntelliJ наверняка) выдадут предупреждение в этой строке и предложат удалить апкастинг).

РЕДАКТИРОВАТЬ: Этот код дает те же результаты, потому что все методы объекта в Java являются виртуальными, что означает, что целевой метод обнаруживается фактическим типом объекта во время выполнения, а не ссылочным типом. Попробуйте сделать callme() статично и посмотрим что получится.

Рассмотрим сценарий: банк - это класс, который предоставляет метод для получения процентной ставки. Но процентная ставка может отличаться в зависимости от банков. Например, банки SBI, ICICI и AXIS предоставляют процентную ставку 8,4%, 7,3% и 9,7%.

class Bank{  
    float getRateOfInterest(){return 0;}  
}

class SBI extends Bank{  
    float getRateOfInterest(){return 8.4f;}  
}

class ICICI extends Bank{  
    float getRateOfInterest(){return 7.3f;}  
}

class AXIS extends Bank{  
    float getRateOfInterest(){return 9.7f;}  
}  

class TestPolymorphism{  
    public static void main(String args[]){  
        Bank b;  
        b=new SBI();  
        System.out.println("SBI Rate of Interest: "+b.getRateOfInterest());  
        b=new ICICI();  
        System.out.println("ICICI Rate of Interest: "+b.getRateOfInterest());  
        b=new AXIS();  
        System.out.println("AXIS Rate of Interest: "+b.getRateOfInterest());  
    }  
}

Выход:

SBI Rate of Interest: 8.4
ICICI Rate of Interest: 7.3
AXIS Rate of Interest: 9.7

Простое объяснение Upcasting это...

Есть 2 класса, один - родительский класс (Demo1), второй - его подкласс (Demo).

  1. Если у метода run1 нет параметров, то вывод метода будет таким же. Там нет использования Upcasting.

     class Demo1{
    
     void run1(){
     System.out.println("hello");  
     }
     }
    
    class Demo extends Demo1 {
    
    void run1(){
    System.out.println("hello1");  
    }
    
    public static void main(String args[]){
    
     Demo d = new Demo();
     d.run1();
    
     Demo1 d1 = new Demo();
     d1.run1();
    
      }
      }
    

    Выход для d.run1(); и d1.run1(); будет таким же.

     output: hello1
    
    1. Если мы используем параметризованную перегрузку, то использование upcasting может быть ясно видно. В этом есть смысл Upcasting.

        class Demo1{
      
        void run1(int a){
      
        System.out.println(a);  
        }
        }
      
       class Demo extends Demo1 {
      
        void run1(String b){
      
        System.out.println(b);  
        }
      
        public static void main(String args[]){
      
        Demo d = new Demo();
        d.run1("b");
      
        Demo1 d1 = new Demo();
        d1.run1(2);
      
      }
      }
      

Выход для d.run1(); и d1.run1(); будет другим.

output: b
        2

Некоторые ответы:

  • Обновление происходит неявно каждый раз, когда вы передаете конкретный объект методу, принимающему более общий объект. В вашем примере, когда вы передаете Dog методу, принимающему Animal, происходит обновление, хотя вы явно не делаете это с помощью скобок.
  • Как уже упоминалось другими здесь, в сценариях перегрузки методов, где у вас есть общие и конкретные методы. В вашем примере, представьте, что метод принимает Собаку, а другой с тем же именем принимает Животное. Если по какой-либо причине вам нужно вызвать общую версию, вам нужно явно отключить ваш объект.
  • Явный апкастинг может использоваться как форма документации; если в какой-то момент в вашем коде вам больше не нужно называть "Собаку" как "Собаку", а продолжать относиться к ней как к "Животному", может быть хорошей идеей отказаться от других, чтобы позволить другим разработчикам понять, что происходит.

В этом примере мы создаем два класса Bike и Splendar. Класс Splendar расширяет класс Bike и переопределяет его метод run(). Мы вызываем метод run по ссылочной переменной класса Parent. Поскольку он ссылается на объект подкласса, а метод подкласса переопределяет метод родительского класса, метод подкласса вызывается во время выполнения.

Поскольку вызов метода определяется JVM, а не компилятором, он называется полиморфизмом времени выполнения.

class Bike{  
  void run(){System.out.println("running");}  
}  
class Splender extends Bike{  
  void run(){System.out.println("running safely with 60km");}  

  public static void main(String args[]){  
    Bike b = new Splender();//upcasting  
    b.run();  
  }  
}  

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

Пример кода:

      //class 1
public class Class1 {
  protected int value;

  public Class1(int value) {
    this.value = value;
  }

  protected int getValue() {
    return value;
  }
}

// class 2
public class Class2 extends Class1 {

  public Class2(int value) {
     super(value);
  }

  @Override
  public int getValue() {
    return super.getValue()+1;
  }

  public int getHundred() {
    return 100;
  }
}



//Main method
public class Main {
  public static void main(String args[]) {
    Class1 cls2 = new Class2(10);
    cls2.getValue();
    cls2.getHundred() // gives compile error
  }
}

Учитывая, что методы объектов Java по умолчанию являются виртуальными, я не вижу никакого смысла в апскейдинге вообще.

Где вы видели учебники, в которых сообщалось, что обновление было необходимо? Это первый раз, когда я слышу об этом для Java. (Обратите внимание, что это имеет значение в C# или C++, но обычно это признак плохого дизайна.)

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