Зниження на Java


179

Оновлення дозволено на 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
Приклад фрагмента коду плюс помилка зробить це кращим питанням для людей, які намагаються вивчити ці поняття.
Боб Крос

3
+1 для коментаря Боба. Питання зовсім не ясно.
Джон Скіт

Я бачу приклад вище взято з velocityreviews.com/forums/t151266-downcasting-problem.html, на який уже є хороші відповіді.
PhiLho

2
@PhiLho - Основним наміром Джоеля було отримати все чудове запитання та відповіді під однією загальною парасолькою. Не має значення, чи питання / код / ​​відповіді вже розміщені на деяких інших сайтах. Я сподіваюся, що ви зрозумієте, інакше послухайте подкасти Джоеля.
Всемогутній

Відредагуйте це так, щоб фрагменти коду були відступними чотирма пробілами. Це виправить форматування.
струнка

Відповіді:


298

Спускання каналів дозволено, коли є можливість його успіху під час виконання:

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

Коли акторський склад (такий, як цей останній) не працює під час виконання, 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.

Object o = new Object(); String s = (String) o;Це добре для мене ..: O Як?
Асиф Муштак

@UnKknown: не повинно. Переконайтесь, що ви дійсно склали та запустили цю версію, і якщо ви все ще можете її відтворити, опублікуйте окреме запитання (з SSCCE ).
Йоахім Зауер

@JoachimSauer, що ти маєш на увазі під цією версією? Я використовую Java 8.
Асиф Муштак

1
@UnKknown: Я маю на увазі, що код, який ви опублікували, не повинен працювати (він буде компілюватись, але викидатиме виняток під час виконання). Ці коментарі не є місцем для налагодження цього. Будь ласка, опублікуйте окреме запитання.
Йоахім Зауер

Як лиття не вдається під час виконання? Встановлює цільовий об'єкт посилання на null? Викидає виняток?
CygnusX1

17

Використовуючи свій приклад, ви можете зробити:

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();
}

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

16

Я вважаю, що це стосується всіх мов, які набираються статично:

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 - це справді цілий ряд, якщо припустити, що це String не має сенсу і дасть несподівані результати, тому потрібно перевірити час виконання та виняток, щоб повідомити середовище виконання, що щось не так.

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

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

Мені подобається слово Дійсно, і я відредагую вашу публікацію, щоб зробити її більш
привабливою

5

Всі ми можемо бачити, що наданий вами код не працюватиме під час виконання. Це тому, що ми знаємо, що вираз ніколи неnew A() може бути типом об’єкта .B

Але компілятор це не бачить. На той момент, коли компілятор перевіряє, чи дозволений амплуа, він просто бачить це:

variable_of_type_B = (B)expression_of_type_A;

І як показали інші, такий вид акторської ролі є цілком законним. Вираз праворуч може дуже добре оцінити об'єкт типу B. Компілятор бачить це Aі Bмає відношення до підтипу, тому при перегляді коду «вираз» код може працювати.

Компілятор не враховує особливий випадок, коли точно знає , який тип об’єкта expression_of_type_Aбуде насправді мати. Він просто розглядає статичний тип як Aі вважає, що динамічний тип може бути Aбудь-яким нащадком A, у тому числі B.


3

У такому випадку чому Java дозволяє скинути канал, якщо він не може бути виконаний під час виконання?

Я вважаю, що це тому, що компілятор не може дізнатися під час компіляції, чи вдалий виступ буде чи ні. Для вашого прикладу просто зрозуміти, що акторський склад буде невдалим, але є й інші часи, коли це не так зрозуміло.

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


2

@ Оригінальний плакат - див. Вбудовані коментарі.

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");
    }
}

2

Downcast працює в тому випадку, коли ми маємо справу з об'єктом, що скидається. Оновлення:

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

Так що тепер ця objValueзмінна завжди може бути downcasted до , intтак як об'єкт , який був відлитий є Integer,

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

але тому, що objValueє Об'єктом, до якого не можна подати, Stringтому що intне може бути передано String.


0

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;
}

Я зберігаю String у пов'язаному списку. Коли я отримую елементи пов'язаного списку, об’єкти повертаються. Для доступу до елементів у вигляді рядків (або будь-яких інших об’єктів класу) мені допомагає downcasting.

Java дозволяє нам складати низхідний код, довіряючи нам, що ми робимо неправильно. Але якщо люди помиляються, це потрапляє під час виконання.


Використання не generic колекцій на Java є еквівалентом void*покажчиків у C ++. Мені це зовсім не здається гарною ідеєю.
Jezor

0

Розглянемо нижченаведений приклад

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


AOne виглядає дещо заплутано. Будь ласка, подумайте про те, щоб змінити назви класів на собак та тварин або щось таке
Kartik Chugh

0

Для того, щоб зробити Java в явному режимі та уникнути виключень під час запуску, скористайтеся таким кодом:

if (animal instanceof Dog) {
  Dog dogObject = (Dog) animal;
}

Тут Animal - це батьківський клас, а Dog - дитячий клас.
instanceof - це ключове слово, яке використовується для перевірки, чи містить опорна змінна заданий тип посилання на об'єкт чи ні.


0

Непридатне перетворення об'єктів неможливе. Тільки

DownCasting1 _downCasting1 = (DownCasting1)((DownCasting2)downCasting1);

можливо

class DownCasting0 {
    public int qwe() {
        System.out.println("DownCasting0");
        return -0;
    }
}

class DownCasting1 extends DownCasting0 {
    public int qwe1() {
        System.out.println("DownCasting1");
        return -1;
    }
}

class DownCasting2 extends DownCasting1 {
    public int qwe2() {
        System.out.println("DownCasting2");
        return -2;
    }
}

public class DownCasting {

    public static void main(String[] args) {

        try {
            DownCasting0 downCasting0 = new DownCasting0();
            DownCasting1 downCasting1 = new DownCasting1();
            DownCasting2 downCasting2 = new DownCasting2();

            DownCasting0 a1 = (DownCasting0) downCasting2;
            a1.qwe(); //good

            System.out.println(downCasting0 instanceof  DownCasting2);  //false
            System.out.println(downCasting1 instanceof  DownCasting2);  //false
            System.out.println(downCasting0 instanceof  DownCasting1);  //false

            DownCasting2 _downCasting1= (DownCasting2)downCasting1;     //good
            DownCasting1 __downCasting1 = (DownCasting1)_downCasting1;  //good
            DownCasting2 a3 = (DownCasting2) downCasting0; // java.lang.ClassCastException

            if(downCasting0 instanceof  DownCasting2){ //false
                DownCasting2 a2 = (DownCasting2) downCasting0;
                a2.qwe(); //error
            }

            byte b1 = 127;
            short b2 =32_767;
            int b3 = 2_147_483_647;
//          long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
            long b4 = 9_223_372_036_854_775_807L;
//          float _b5 = 3.4e+038; //double default
            float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
            double b6 = 1.7e+038;
            double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits

            long c1 = b3;
            int c2 = (int)b4;

            //int       4 bytes     Stores whole numbers from -2_147_483_648 to 2_147_483_647
            //float     4 bytes     Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
            float c3 = b3; //logic error
            double c4 = b4; //logic error


        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.