süper sınıftan alt sınıfa açık döküm


161
public class Animal {
    public void eat() {}
}

public class Dog extends Animal {
    public void eat() {}

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = (Dog) animal;
    }
}

Ödev Dog dog = (Dog) animal;bir derleme hatası oluşturmaz, ancak çalışma zamanında bir a oluşturur ClassCastException. Derleyici bu hatayı neden algılayamıyor?


51
Derleyiciye hatayı tespit ETMEMESİNİ söylüyorsunuz.
Mauricio

Yanıtlar:


326

Bir döküm kullanarak temelde derleyiciye "bana güvenin. Profesyonelim, ne yaptığımı biliyorum ve bunu garanti edemeseniz de, bu animaldeğişkenin kesinlikle olduğunu söylüyorum köpek olacak. "

Hayvan aslında bir köpek olmadığından (bir hayvan, yapabilirsin Animal animal = new Dog();ve bir köpek olurdu) VM çalışma zamanında bir istisna atıyor çünkü güvenini ihlal ettin (derleyiciye her şeyin yoluna gireceğini söyledin ve değil!)

Derleyici, her şeyi körü körüne kabul etmekten biraz daha akıllıdır, eğer farklı miras hiyerarşilerindeki nesneleri (örneğin bir Köpeği bir String'e döktüğünüzde) denerseniz ve asla işe yaramayacağını bildiği için size geri atar.

Temel olarak derleyicinin şikayet etmesini durdurduğunuz için, her yayınladığınızda, bir if ifadesinde (veya bu yönde bir şey) ClassCastExceptionkullanarak instanceofbir hataya neden olmayacağınızı kontrol etmek önemlidir .


Ancak köpeğe ihtiyacım sayesinde Hayvan dan değilse, değil işi :) uzatmak
delive

66
Ne kadar dramatik bir ses çıkardığını seviyorum
Hendra Anggrian

3
Yapmanız Tabii @delive ama söz gereği, Dog yok uzanmaktadır Animal!
Michael Berry

52

Teorik Çünkü Animal animal edebilir bir köpek olmak:

Animal animal = new Dog();

Genel olarak, downcasting iyi bir fikir değildir. Bundan kaçınmalısınız. Kullanırsanız, bir çek eklemeniz daha iyi olur:

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

ancak aşağıdaki kod derleme hatası oluşturur Köpek köpek = yeni Hayvan (); (ancak uyumsuz türler). ancak bu durumda derleyici Hayvan'ı bir süper sınıf ve Köpek bir alt sınıf olarak tanımlar. öyleyse atama yanlıştır. ama Köpek köpeği = (Köpek) hayvanı döktüğümüzde; kabul eder. lütfen bu konuda bana açıkla
saravanan

3
evet, çünkü Hayvan süper sınıftır. Her hayvan bir Köpek değildir, değil mi? Sınıflara yalnızca türlerine veya üst türlerine göre başvurabilirsiniz. Alt türleri değil.
Bozho

43

Bu tür bir ClassCastException durumundan kaçınmak için, eğer varsa:

class A
class B extends A

B'de A nesnesini alan bir kurucu tanımlayabilirsiniz. Bu şekilde "döküm" yapabiliriz:

public B(A a) {
    super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
    // If B class has more attributes, then you would initilize them here
}

24

Michael Berry tarafından verilen cevabı detaylandırarak.

Dog d = (Dog)Animal; //Compiles but fails at runtime

Burada derleyici diyorsun ki "Güven bana. Biliyorum dbir Dognesneye gerçekten atıfta bulunuyor" ama olmasa da. Unutmayın derleyici bir mahzun yaparken bize güvenmek zorunda .

Derleyici yalnızca bildirilen başvuru türünü bilir. Çalışma zamanında JVM, nesnenin gerçekte ne olduğunu bilir.

Öyleyse, çalışma zamanında JVM Dog d, aslında Animalbir Dognesneye değil bir nesneye atıfta bulunduğunu anladığında diyor. Hey ... derleyiciye yalan söyledin ve büyük bir yağ atıyorsun ClassCastException.

Bu yüzden aşağı iniş yapıyorsanız, instanceofvidalanmayı önlemek için test kullanmalısınız .

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

Şimdi aklımıza bir soru geliyor. Cehennem derleyicisi nihayet bir atmak için mahzun izin veriyor java.lang.ClassCastException?

Cevap, derleyicinin yapabileceği, iki türün aynı miras ağacında olduğunu doğrulamaktır, bu nedenle, downcast'ten önce hangi kodun gelebileceğine bağlı olarak animal, türün olması mümkündür dog.

Derleyici, çalışma zamanında mümkün olabilecek şeylere izin vermelidir.

Aşağıdaki kod snipet'ini düşünün:

public static void main(String[] args) 
{   
    Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
    Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
    d.eat();

}

private static Animal getMeAnAnimal()
{
    Animal animal = new Dog();
    return animal;
}

Ancak, derleyici, almanın mümkün olmayacağından emin olursa, derleme başarısız olur. IE Farklı miras hiyerarşilerinde nesneler yayınlamaya çalışırsanız

String s = (String)d; // ERROR : cannot cast for Dog to String

Alt sürümün tersine, üst sürüm, örtük olarak çalışır, çünkü üst üste çıkardığınızda, alt sürümün tersine çağırabileceğiniz yöntem sayısını dolaylı olarak kısıtlarsınız; bu, daha sonra daha spesifik bir yöntem çağırmak isteyebileceğiniz anlamına gelir.

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

Yukarıdaki upcast her ikisi de istisnasız iyi çalışacaktır, çünkü bir Hayvan IS, bir Hayvan yapabilirsiniz anithing, bir köpek yapabilirsiniz. Ama bu tam tersi değil.


1

Örnek türünüz bir Hayvan olduğundan kod bir derleme hatası oluşturur :

Animal animal=new Animal();

Java'da çeşitli nedenlerle downcasting'e izin verilmez. Ayrıntılar için buraya bakın.


5
Derleme hatası yok, sorusunun nedeni bu
Clarence Liu

1

Açıklandığı gibi, bu mümkün değildir. Alt sınıfın bir yöntemini kullanmak istiyorsanız, yöntemi üst sınıfa ekleme (boş olabilir) ve polimorfizm sayesinde istediğiniz davranışı alan alt sınıflardan (alt sınıf) çağrı yapma olasılığını değerlendirin. Bu nedenle, d.method () öğesini çağırdığınızda çağrı dökümden başarılı olur, ancak nesnenin bir köpek olmaması durumunda, bir sorun olmaz.


0

@Caumons'un cevabını geliştirmek için:

Bir baba sınıfının birçok çocuğu olduğunu ve bu sınıfa ortak bir alan eklemeye ihtiyaç olduğunu düşünün. Bahsedilen yaklaşımı düşünürseniz, her çocuk sınıfına tek tek gitmeli ve yeni alan için yapıcılarını yeniden düzenlemelisiniz. bu nedenle bu çözüm bu senaryoda umut verici bir çözüm değildir

Şimdi bu çözüme bir göz atın.

Bir baba her çocuktan kendi kendine bir nesne alabilir. İşte bir baba sınıfı:

public class Father {

    protected String fatherField;

    public Father(Father a){
        fatherField = a.fatherField;
    }

    //Second constructor
    public Father(String fatherField){
        this.fatherField = fatherField;
    }

    //.... Other constructors + Getters and Setters for the Fields
}

İşte baba kurucularından birini, bu durumda yukarıda belirtilen kurucuyu uygulaması gereken çocuk sınıfımız:

public class Child extends Father {

    protected String childField;

    public Child(Father father, String childField ) {
        super(father);
        this.childField = childField;
    }

    //.... Other constructors + Getters and Setters for the Fields

    @Override
    public String toString() {
        return String.format("Father Field is: %s\nChild Field is: %s", fatherField, childField);
    }
}

Şimdi uygulamayı test ediyoruz:

public class Test {
    public static void main(String[] args) {
        Father fatherObj = new Father("Father String");
        Child child = new Child(fatherObj, "Child String");
        System.out.println(child);
    }
}

Ve işte sonuç:

Baba Alanı: Baba Dizesi

Çocuk Alanı: Çocuk Dizesi

Artık çocuk kodlarının kırılmasından endişe etmeden baba sınıfına kolayca yeni alanlar ekleyebilirsiniz;

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.