Даункинг в Java - PullRequest
       34

Даункинг в Java

167 голосов
/ 19 декабря 2008

В Java разрешено обновление, однако дача дает ошибку компиляции.

Ошибка компиляции может быть устранена путем добавления приведения, но она все равно прервется во время выполнения.

В этом случае, почему Java допускает даункастинг, если он не может быть выполнен во время выполнения?
Есть ли практическое применение этой концепции?

public class demo {
  public static void main(String a[]) {
      B b = (B) new A(); // compiles with the cast, 
                         // but runtime exception - java.lang.ClassCastException
  }
}

class A {
  public void draw() {
    System.out.println("1");
  }

  public void draw1() {
    System.out.println("2");
  }
}

class B extends A {
  public void draw() {
    System.out.println("3");
  }
  public void draw2() {
    System.out.println("4");
  }
}

Ответы [ 9 ]

282 голосов
/ 19 декабря 2008

Даункинг разрешен, когда есть вероятность, что он преуспеет во время выполнения:

Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String

В некоторых случаях это не удастся:

Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String

В других это будет работать:

Object o = "a String";
String s = (String) o; // this will work, since o references a String

Если во время выполнения происходит сбой приведения (например, последнего), ClassCastException будет брошено.

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

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
16 голосов
/ 19 декабря 2008

Используя ваш пример, вы можете сделать:

public void doit(A a) {
    if(a instanceof B) {
        // needs to cast to B to access draw2 which isn't present in A
        // note that this is probably not a good OO-design, but that would
        // be out-of-scope for this discussion :)
        ((B)a).draw2();
    }
    a.draw();
}
15 голосов
/ 19 декабря 2008

Я считаю, что это относится ко всем статически типизированным языкам:

String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String

Typecast говорит: предположите, что это ссылка на класс приведения и используйте его как таковой. Теперь предположим, что o является действительно целым числом, предполагая, что это строка не имеет смысла и даст неожиданные результаты, поэтому требуется проверка во время выполнения и исключение, чтобы уведомить среду выполнения о том, что что-то не так .

При практическом использовании вы можете написать код, работающий с более общим классом, но привести его к подклассу, если вы знаете, что это за подкласс, и вам нужно обращаться с ним как таковым. Типичным примером является переопределение Object.equals (). Предположим, у нас есть класс для автомобиля:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}
5 голосов
/ 19 декабря 2008

Мы все видим, что предоставленный вами код не будет работать во время выполнения. Это потому, что мы знаем, что выражение new A() не может никогда быть объектом типа B.

Но это не так, как видит это компилятор. К тому времени, когда компилятор проверяет, разрешено ли приведение, он просто видит это:

variable_of_type_B = (B)expression_of_type_A;

И, как показали другие, подобный состав совершенно законен. Выражение справа вполне может вычислить объект типа B. Компилятор видит, что A и B имеют отношение подтипа, поэтому в представлении «выражение» кода приведение может работать.

Компилятор не учитывает особый случай, когда он точно знает , какой тип объекта expression_of_type_A действительно будет иметь. Он просто видит статический тип как A и считает, что динамический тип может быть A или любым потомком A, включая B.

3 голосов
/ 19 декабря 2008

В таком случае, почему Java допускает даункастинг, если он не может быть выполнен во время выполнения?

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

Например, представьте, что все типы B, C и D расширяют тип A, а затем метод public A getSomeA() возвращает экземпляр B, C или D в зависимости от случайно сгенерированного числа. Компилятор не может знать, какой именно тип времени выполнения будет возвращен этим методом, поэтому, если впоследствии вы приведете результаты к B, нет способа узнать, будет ли приведение выполнено успешно (или не получится). Поэтому компилятор должен предполагать, что приведение завершится успешно.

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

@ Оригинальный постер - см. Встроенные комментарии.

public class demo 
{
    public static void main(String a[]) 
    {
        B b = (B) new A(); // compiles with the cast, but runtime exception - java.lang.ClassCastException 
        //- A subclass variable cannot hold a reference to a superclass  variable. so, the above statement will not work.

        //For downcast, what you need is a superclass ref containing a subclass object.
        A superClassRef = new B();//just for the sake of illustration
        B subClassRef = (B)superClassRef; // Valid downcast. 
    }
}

class A 
{
    public void draw() 
    {
        System.out.println("1");
    }

    public void draw1() 
    {
        System.out.println("2");
    }
}

class B extends A 
{
    public void draw() 
    {
        System.out.println("3");
    }

    public void draw2() 
    {
        System.out.println("4");
    }
}
1 голос
/ 01 июля 2013

Downcast работает в том случае, когда мы имеем дело с перемещаемым объектом. Приведение к базовому типу:

int intValue = 10;
Object objValue = (Object) intvalue;

Так что теперь эта переменная objValue всегда может быть понижена до int, потому что объект, который был приведен, является Integer,

int oldIntValue = (Integer) objValue;
// can be done 

но поскольку objValue является объектом, его нельзя привести к String, поскольку int нельзя преобразовать к String.

0 голосов
/ 08 июня 2014

Рассмотрим приведенный ниже пример

public class ClastingDemo {

/**
 * @param args
 */
public static void main(String[] args) {
    AOne obj = new Bone();
    ((Bone) obj).method2();
}
}

class AOne {
public void method1() {
    System.out.println("this is superclass");
}
}


 class Bone extends AOne {

public void method2() {
    System.out.println("this is subclass");
}
}

здесь мы создаем объект подкласса Bone и назначаем его ссылке на суперкласс AOne, а теперь ссылка на суперкласс не знает о методе method2 в подклассе, т.е. Bone во время компиляции. Поэтому нам нужно уменьшить эту ссылку суперкласса до ссылки на подкласс, чтобы результирующая ссылка могла знать о наличии методов в подклассе, т.е. Bone

0 голосов
/ 13 октября 2013

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

private static String printAll(LinkedList c)
{
    Object arr[]=c.toArray();
    String list_string="";
    for(int i=0;i<c.size();i++)
    {
        String mn=(String)arr[i];
        list_string+=(mn);
    }
    return list_string;
}

Я храню строку в связанном списке. Когда я получаю элементы связанного списка, объекты возвращаются. Чтобы получить доступ к элементам в виде строк (или любых других объектов класса), мне помогает downcasting.

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

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