Downcasting di Jawa

179

Upcasting diperbolehkan di Java, namun downcasting memberikan kesalahan kompilasi.

Kesalahan kompilasi dapat dihapus dengan menambahkan gips tetapi tetap akan rusak saat runtime.

Dalam hal ini mengapa Java memungkinkan downcasting jika tidak dapat dijalankan pada saat runtime?
Apakah ada kegunaan praktis untuk konsep ini?

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");
  }
}
pejuang
sumber
9
Contoh potongan kode ditambah kesalahan akan menjadikan ini pertanyaan yang lebih baik bagi orang yang mencoba mempelajari konsep.
Bob Cross
3
+1 untuk komentar Bob. Pertanyaannya sama sekali tidak jelas.
Jon Skeet
Saya melihat contoh di atas diambil dari velocityreviews.com/forums/t151266-downcasting-problem.html yang sudah memiliki beberapa jawaban bagus.
PhiLho
2
@PhiLho - Niat utama Joel adalah untuk mendapatkan semua pertanyaan dan jawaban hebat di bawah satu payung umum. Tidak masalah jika pertanyaan / kode / jawaban sudah diposting di beberapa situs lain. Saya harap Anda mengerti maksudnya, kalau tidak dengarkan podcast Joel.
Mahakuasa
Harap edit ini sehingga cuplikan kode semua indentasi oleh empat spasi. Itu akan memperbaiki pemformatan.
langsing

Jawaban:

298

Downcasting diperbolehkan ketika ada kemungkinan berhasil pada saat run time:

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

Dalam beberapa kasus ini tidak akan berhasil:

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

Ketika seorang pemain (seperti yang terakhir ini) gagal saat runtime a ClassCastExceptionakan dilempar.

Dalam kasus lain, ini akan berhasil:

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

Perhatikan bahwa beberapa pemain akan dianulir pada waktu kompilasi, karena mereka tidak akan pernah berhasil sama sekali:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
Joachim Sauer
sumber
Object o = new Object(); String s = (String) o;Ini bekerja dengan baik untuk saya ..: O Bagaimana?
Asif Mushtaq
@UnKnown: seharusnya tidak. Periksa ulang apakah Anda benar-benar mengkompilasi dan menjalankan versi itu dan jika Anda masih dapat mereproduksinya, posting pertanyaan terpisah (dengan SSCCE ).
Joachim Sauer
@ JoachimSauer apa yang Anda maksud dengan versi itu? Saya menggunakan Java 8.
Asif Mushtaq
1
@ Unnknown: Maksud saya kode yang Anda posting tidak boleh berjalan (itu akan dikompilasi, tetapi melemparkan pengecualian saat runtime). Komentar-komentar ini bukan ruang untuk men-debug itu. Silakan kirim pertanyaan terpisah.
Joachim Sauer
Bagaimana casting gagal saat runtime? Menetapkan referensi objek target ke nol? Melempar pengecualian?
CygnusX1
17

Dengan menggunakan contoh Anda, Anda bisa melakukan:

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();
}
Rolf Rander
sumber
Saya baru belajar pentingnya contoh ketika kelas abstrak saya sedang diperluas oleh beberapa kelas dan saya ingin menggunakan metode eksklusif dari kelas-kelas itu, sambil merujuk pada tipe kelas abstrak. Tidak menggunakan instance dari saya memiliki pengecualian pemain kelas
Tarun
16

Saya yakin ini berlaku untuk semua bahasa yang diketik secara statis:

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 secara efektif mengatakan: anggap ini adalah referensi ke kelas pemain dan gunakanlah seperti itu. Sekarang, katakanlah o benar - benar Integer, dengan anggapan ini adalah String tidak masuk akal dan akan memberikan hasil yang tidak terduga, sehingga perlu ada pemeriksaan runtime dan pengecualian untuk memberi tahu lingkungan runtime bahwa ada sesuatu yang salah.

Dalam penggunaan praktis, Anda dapat menulis kode yang bekerja pada kelas yang lebih umum, tetapi melemparkannya ke subkelas jika Anda tahu apa itu subkelas dan perlu memperlakukannya seperti itu. Contoh tipikal adalah menimpa Object.equals (). Asumsikan kita memiliki kelas untuk Mobil:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}
Rolf Rander
sumber
Saya suka kata Sungguh dan saya akan mengedit posting Anda untuk membuatnya lebih jelas
Charaf JRA
5

Kita semua dapat melihat bahwa kode yang Anda berikan tidak akan berfungsi pada saat dijalankan. Itu karena kita tahu bahwa ekspresi new A()dapat tidak pernah menjadi sebuah objek dari tipe B.

Tapi bukan itu yang dilihat kompiler. Pada saat kompiler memeriksa apakah para pemain diperbolehkan, ia hanya melihat ini:

variable_of_type_B = (B)expression_of_type_A;

Dan seperti yang telah ditunjukkan orang lain, para pemeran semacam itu sangat sah. Ekspresi di sebelah kanan bisa sangat baik mengevaluasi ke objek bertipe B. Kompilator melihat itu Adan Bmemiliki hubungan subtipe, jadi dengan tampilan "ekspresi" kode, para pemain mungkin bekerja.

Compiler tidak mempertimbangkan case khusus ketika ia tahu persis tipe objek apa yang expression_of_type_Abenar-benar akan miliki. Itu hanya melihat tipe statis sebagai Adan menganggap tipe dinamis bisa Aatau keturunan apa pun A, termasuk B.

Rob Kennedy
sumber
3

Dalam hal ini mengapa Java memungkinkan downcasting jika tidak dapat dijalankan pada saat runtime?

Saya percaya ini karena tidak ada cara bagi kompiler untuk mengetahui pada waktu kompilasi apakah para pemeran akan berhasil atau tidak. Sebagai contoh Anda, mudah untuk melihat bahwa para pemain akan gagal, tetapi ada saat-saat di mana itu tidak begitu jelas.

Sebagai contoh, bayangkan bahwa tipe B, C, dan D semuanya memperpanjang tipe A, dan kemudian sebuah metode public A getSomeA()mengembalikan sebuah instance dari B, C atau D tergantung pada angka yang dihasilkan secara acak. Kompiler tidak dapat mengetahui tipe run-time yang tepat yang akan dikembalikan oleh metode ini, jadi jika Anda kemudian memberikan hasilnya B, tidak ada cara untuk mengetahui apakah para pemeran akan berhasil (atau gagal). Oleh karena itu kompiler harus mengasumsikan gips akan berhasil.

matt b
sumber
2

@ Poster Asli - lihat komentar sebaris.

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");
    }
}
Alok Sharma
sumber
2

Downcast bekerja dalam kasus ketika kita berhadapan dengan objek upcasted. Siaran:

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

Jadi sekarang objValuevariabel ini selalu dapat diturunkan intkarena objek yang dilemparkan adalah Integer,

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

tetapi karena objValuemerupakan Obyek, ia tidak dapat dilemparkan ke Stringkarena inttidak dapat dilemparkan ke String.

Uday Reddy
sumber
0

Downcasting sangat berguna dalam cuplikan kode berikut yang saya gunakan ini sepanjang waktu. Dengan demikian membuktikan bahwa downcasting bermanfaat.

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

Saya menyimpan String di Daftar Tertaut. Ketika saya mengambil elemen Daftar Tertaut, Objek dikembalikan. Untuk mengakses elemen sebagai Strings (atau Obyek Kelas lainnya), downcasting membantu saya.

Java memungkinkan kita untuk mengkompilasi kode downcast mempercayai kita bahwa kita melakukan hal yang salah. Namun jika manusia membuat kesalahan, itu tertangkap saat runtime.

Drishti
sumber
Menggunakan koleksi non-generik di Jawa adalah setara dengan void*pointer di C ++. Bagi saya itu sama sekali bukan ide yang bagus.
Jezor
0

Perhatikan contoh di bawah ini

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

di sini kita membuat objek dari subclass Bone dan menugaskannya ke referensi super kelas AOne dan sekarang referensi superclass tidak tahu tentang metode metode2 dalam subkelas yaitu Bone selama waktu kompilasi. referensi yang dihasilkan dapat mengetahui tentang adanya metode dalam subkelas yaitu Bone

ZohebSiddiqui
sumber
AOne terlihat agak membingungkan. Silakan pertimbangkan mengubah nama kelas Anda menjadi Anjing dan Hewan atau sesuatu
Kartik Chugh
0

Untuk melakukan downcasting di Java, dan menghindari pengecualian run-time, ambil referensi kode berikut:

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

Di sini, Hewan adalah kelas induk dan Anjing adalah kelas anak.
instanceof adalah kata kunci yang digunakan untuk memeriksa apakah variabel referensi berisi jenis referensi objek tertentu atau tidak.

pengguna11949964
sumber
0

Transformasi objek yang tidak dapat dilakukan dengan sendirinya tidak dimungkinkan. Hanya

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

mungkin

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

}
Aliaksandr Shpak
sumber