Вопрос-ответ

Downcasting in Java

Даункастинг в Java

В 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");
}
}
Переведено автоматически
Ответ 1

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

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

При сбое приведения (такого, как это последнее) во время выполнения будет выдан a ClassCastException.

В других случаях это будет работать:

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

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

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
Ответ 2

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

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();
}
Ответ 3

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

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

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

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

@Override
boolean equals(Object o) {
if(!(o instanceof Car)) return false;
Car other = (Car)o;
// compare this to other and return
}
Ответ 4

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

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

variable_of_type_B = (B)expression_of_type_A;

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

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

java