CodeGym /Blog Java /rawak /Pelebaran dan penyempitan jenis rujukan
John Squirrels
Tahap
San Francisco

Pelebaran dan penyempitan jenis rujukan

Diterbitkan dalam kumpulan
Hai! Dalam pelajaran yang lalu, kami membincangkan pemutus jenis primitif. Mari kita ingat secara ringkas apa yang dibincangkan. Pelebaran dan penyempitan jenis rujukan - 1Kami membayangkan jenis primitif (dalam kes ini, jenis berangka) sebagai anak patung bersarang yang berbeza dalam saiz mengikut jumlah memori yang didudukinya. Seperti yang anda akan ingat, meletakkan anak patung yang lebih kecil di dalam yang lebih besar adalah mudah dalam kehidupan sebenar dan dalam pengaturcaraan Java.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
Ini ialah contoh penukaran automatik atau pelebaran . Ia berlaku dengan sendirinya, jadi anda tidak perlu menulis kod tambahan. Akhirnya, kami tidak melakukan apa-apa yang luar biasa: kami hanya meletakkan anak patung yang lebih kecil menjadi anak patung yang lebih besar. Perkara lain jika kita cuba melakukan sebaliknya dan meletakkan anak patung Rusia yang lebih besar ke dalam anak patung yang lebih kecil. Anda tidak boleh melakukannya dalam kehidupan sebenar, tetapi dalam pengaturcaraan anda boleh. Tetapi ada satu nuansa. Jika kita cuba memasukkan intke dalam shortpembolehubah, perkara tidak berjalan dengan lancar untuk kita. Lagipun, shortpembolehubah hanya memegang 16 bit maklumat, tetapi intmenduduki 32 bit! Akibatnya, nilai yang diluluskan diherotkan. Pengkompil akan memberi kami ralat (' Kawan, anda melakukan sesuatu yang mencurigakan!'). Tetapi jika kami menyatakan secara eksplisit jenis yang kami tukarkan nilai kami, ia akan meneruskan dan melaksanakan operasi.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Itulah yang kami lakukan dalam contoh di atas. Operasi telah dilakukan, tetapi kerana shortpembolehubah boleh menampung hanya 16 daripada 32 bait, nilai akhir diputarbelitkan dan kami mendapat nombor -27008 . Operasi sedemikian dipanggil penukaran eksplisit, atau penyempitan .

Contoh pelebaran dan penyempitan jenis rujukan

Sekarang mari kita bercakap tentang pengendali yang sama digunakan bukan untuk jenis primitif, tetapi untuk objek dan pembolehubah rujukan ! Bagaimanakah ini berfungsi di Jawa? Ia sebenarnya agak mudah. Terdapat objek yang tidak berkaitan. Adalah logik untuk menganggap bahawa mereka tidak boleh ditukar kepada satu sama lain, secara eksplisit mahupun automatik:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog(); // Error!

   }

}
Di sini, sudah tentu, kita mendapat ralat. Kelas Catdan Dogtidak berkaitan antara satu sama lain, dan kami belum menulis 'penukar' untuk berpindah dari satu ke yang lain. Masuk akal bahawa kita tidak boleh melakukan ini: pengkompil tidak tahu cara menukar objek ini daripada satu jenis kepada yang lain. Jika objek itu berkaitan, baik, itu perkara lain! Berkaitan bagaimana? Di atas semua, melalui warisan. Mari cuba gunakan warisan untuk mencipta sistem kelas yang kecil. Kami akan mempunyai kelas biasa untuk mewakili haiwan:

public class Animal {

   public void introduce() {

       System.out.println("I'm Animal");
   }
}
Semua orang tahu bahawa haiwan boleh dijinakkan (haiwan peliharaan) atau liar:

public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("I'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("I'm Pet");
   }
}
Sebagai contoh, ambil anjing - kami mempunyai anjing domestik dan anjing hutan:

public class Dog extends Pet {

   public void introduce() {

       System.out.println("I'm Dog");
   }
}



public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println ("I'm Coyote");
   }
}
Kami secara khusus memilih kelas yang paling asas untuk menjadikannya lebih mudah difahami. Kami tidak memerlukan sebarang medan, dan satu kaedah sudah memadai. Mari cuba laksanakan kod ini:

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Apa yang anda fikir akan dipaparkan pada konsol? Adakah introducekaedah kelas Petatau Animalkelas akan digunakan? Cuba untuk membenarkan jawapan anda sebelum anda meneruskan pembacaan. Dan inilah hasilnya! Saya Binatang Kenapa kita dapat itu? Semuanya mudah. Kami mempunyai pembolehubah induk dan objek keturunan. Dengan menulis,

Animal animal = new Pet();
kami meluaskan Petrujukan dan memberikannya kepada Animalpembolehubah. Seperti jenis primitif, jenis rujukan diluaskan secara automatik di Jawa. Anda tidak perlu menulis kod tambahan untuk mewujudkannya. Sekarang kita mempunyai objek keturunan yang diberikan kepada rujukan induk. Akibatnya, kita melihat bahawa panggilan kaedah dibuat pada kelas keturunan. Jika anda masih tidak memahami sepenuhnya mengapa kod ini berfungsi, tulis semula dalam bahasa biasa:

Animal animal = new DomesticatedAnimal();
Tidak ada masalah dengan ini, bukan? Bayangkan bahawa ini adalah kehidupan sebenar, dan rujukannya hanyalah label kertas dengan tulisan 'Haiwan'. Jika anda mengambil sekeping kertas itu dan melekatkannya pada kolar mana-mana haiwan peliharaan, semuanya akan betul. Lagipun, mana-mana haiwan peliharaan adalah haiwan! Proses terbalik — memindahkan pokok warisan kepada keturunan — semakin mengecil:

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Seperti yang anda lihat, di sini kami dengan jelas menunjukkan kelas yang kami ingin tukar objek kami. Kami sebelum ini mempunyai WildAnimalpembolehubah, dan kini kami mempunyai Coyote, yang lebih rendah pada pokok warisan. Adalah masuk akal bahawa tanpa petunjuk yang jelas pengkompil tidak akan membenarkan operasi sedemikian, tetapi jika kami menunjukkan jenis dalam kurungan, maka semuanya berfungsi. Pelebaran dan penyempitan jenis rujukan - 2Pertimbangkan satu lagi contoh yang lebih menarik:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal(); // Error!
   }
}
Pengkompil menjana ralat! Tapi kenapa? Kerana anda cuba menetapkan objek induk kepada rujukan keturunan. Dengan kata lain, anda cuba melakukan sesuatu seperti ini:

DomesticatedAnimal domesticatedAnimal = new Animal();
Baiklah, mungkin semuanya akan berfungsi jika kita menyatakan secara jelas jenis yang kita cuba tukarkan? Itu berkesan dengan nombor — Mari cuba! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Pengecualian dalam benang "utama" java.lang.ClassCastException: Haiwan tidak boleh dihantar ke Ralat Binatang! Pengkompil tidak menjerit kepada kami kali ini, tetapi kami berakhir dengan pengecualian. Kami sudah tahu sebabnya: kami cuba menetapkan objek induk kepada rujukan keturunan. Tetapi kenapa sebenarnya anda tidak boleh melakukannya? Kerana bukan semua Haiwan adalah Haiwan Terternak. Anda mencipta Animalobjek dan cuba memberikannya kepada Petpembolehubah. Coyote juga adalah Animal, tetapi ia bukan Pet. Dalam erti kata lain, apabila anda menulis

Pet pet = (Pet) new Animal();
new Animal()boleh mewakili mana-mana haiwan, tidak semestinya haiwan peliharaan! Sememangnya, Pet petpembolehubah anda hanya sesuai untuk menyimpan Haiwan Kesayangan (dan keturunannya) dan bukan sebarang jenis haiwan. Itulah sebabnya pengecualian Java khas, ClassCastException, dicipta untuk kes di mana ralat berlaku semasa menghantar kelas. Mari kita semak semula untuk membuat perkara lebih jelas. Rujukan induk boleh menunjukkan contoh kelas keturunan:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Sebagai contoh, di sini kita tidak mempunyai masalah. Kami mempunyai Petobjek yang dirujuk oleh Petpembolehubah. Kemudian, Animalrujukan menunjuk pada objek yang sama. Selepas itu, kita tukar animalkepada Pet. Ngomong-ngomong, mengapa ia berfungsi untuk kami? Kali terakhir kami mendapat pengecualian! Kerana kali ini objek asal kita ialah Pet!

Pet pet = new Pet();
Tetapi dalam contoh terakhir, ia adalah Animalobjek:

Pet pet = (Pet) new Animal();
Anda tidak boleh menetapkan objek nenek moyang kepada pembolehubah turunan. Anda boleh melakukan sebaliknya.
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION