JavaRush /Blog Java /Random-VI /Mở rộng và thu hẹp các loại tham chiếu

Mở rộng và thu hẹp các loại tham chiếu

Xuất bản trong nhóm
Xin chào! Trong một trong những bài giảng trước, chúng ta đã thảo luận về việc đúc các kiểu nguyên thủy. Chúng ta hãy nhớ ngắn gọn những gì chúng ta đã nói về. Mở rộng và thu hẹp các loại tham chiếu - 1Chúng tôi biểu diễn các kiểu nguyên thủy (trong trường hợp này là số) dưới dạng búp bê lồng nhau tùy theo dung lượng bộ nhớ mà chúng chiếm giữ. Như bạn nhớ, việc đặt một con búp bê làm tổ nhỏ hơn vào một con búp bê lớn hơn sẽ đơn giản cả trong đời thực lẫn trong lập trình Java.
public class Main {
   public static void main(String[] args) {
        short smallNumber = 100;
        int bigNumber =  smallNumber;
        System.out.println(bigNumber);
   }
}
Đây là một ví dụ về chuyển đổi tự động hoặc tiện ích mở rộng . Nó tự xảy ra nên không cần phải viết thêm mã. Cuối cùng, chúng tôi không làm điều gì bất thường: chúng tôi chỉ đơn giản đặt một con búp bê làm tổ nhỏ hơn vào một con búp bê làm tổ lớn hơn. Sẽ là một vấn đề khác nếu chúng ta cố gắng làm ngược lại và đặt một con búp bê matryoshka lớn vào một con búp bê nhỏ hơn. Điều này không thể thực hiện được trong cuộc sống, nhưng trong lập trình thì có thể thực hiện được. Nhưng có một lưu ý. Nếu chúng ta cố gắng đặt một giá trị intvào một biến short, nó sẽ không dễ dàng như vậy. Rốt cuộc, chỉ có 16 bit thông tin có thể vừa với một biến short, nhưng giá trị intphải mất 32 bit! Kết quả là giá trị truyền đi sẽ bị méo. Trình biên dịch sẽ báo lỗi cho chúng ta (“ anh bạn, bạn đang làm điều gì đó đáng ngờ! ”), nhưng nếu chúng ta chỉ định rõ ràng loại mà chúng ta đang chuyển giá trị của mình sang thì nó vẫn sẽ thực hiện thao tác như vậy.
public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Trong ví dụ trên, chúng tôi đã làm điều đó. Thao tác đã hoàn tất, nhưng vì shortchỉ có 16 trong số 32 bit phù hợp với biến nên giá trị cuối cùng bị sai lệch và kết quả là chúng tôi nhận được số -27008 . Hoạt động này được gọi là chuyển đổi rõ ràng hoặc thu hẹp .

Ví dụ về mở rộng và thu hẹp các loại tham chiếu

Bây giờ chúng ta sẽ nói về các thao tác tương tự, nhưng không áp dụng cho các kiểu nguyên thủy mà cho các đối tượng và biến tham chiếu ! Tính năng này hoạt động như thế nào trong Java? Thực sự khá đơn giản. Có những đồ vật không liên quan đến nhau. Sẽ là hợp lý khi cho rằng chúng không thể được chuyển đổi sang nhau một cách rõ ràng hoặc tự động:
public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

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

   }

}
Tất nhiên ở đây chúng ta sẽ gặp lỗi. Các lớp Catkhông Dogliên quan với nhau và chúng tôi chưa viết một bộ chuyển đổi trực tuyến nào từ lớp này sang lớp khác. Thật hợp lý khi chúng ta không thể làm điều này: trình biên dịch không biết cách chuyển đổi các đối tượng này với nhau. Đó là một vấn đề khác nếu các đối tượng được kết nối với nhau! Làm sao? Trước hết, sử dụng tính kế thừa. Hãy thử tạo một hệ thống lớp nhỏ có tính kế thừa. Chúng ta sẽ có một lớp chung đại diện cho động vật:
public class Animal {

   public void introduce() {

       System.out.println("i'm Animal");
   }
}
Động vật, như bạn biết, là động vật nuôi và hoang dã:
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");
   }
}
Ví dụ: hãy lấy chó - chó nhà và chó sói:
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");
   }
}
Các lớp học của chúng tôi cố tình là nguyên thủy nhất để làm cho chúng dễ nhận biết hơn. Chúng tôi không thực sự cần các trường ở đây và một phương pháp là đủ. Hãy thử chạy đoạn mã sau:
public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Bạn nghĩ gì sẽ được xuất ra bảng điều khiển? introduceLớp Pethoặc phương thức lớp có hoạt động không Animal? Cố gắng biện minh cho câu trả lời của bạn trước khi tiếp tục đọc. Và đây là kết quả! tôi là Pet Tại sao câu trả lời lại thành ra như vậy? Nó đơn giản. Chúng ta có một biến cha và một đối tượng con. Bằng cách viết:
Animal animal = new Pet();
Chúng tôi đã mở rộng kiểu tham chiếuPet và lưu trữ đối tượng của nó trong một biến Animal. Giống như các kiểu nguyên thủy, việc mở rộng các kiểu tham chiếu trong Java được thực hiện tự động. Không cần phải viết thêm mã cho việc này. Bây giờ chúng ta có một đối tượng con được gắn vào tham chiếu cha và kết quả là chúng ta thấy rằng phương thức này được gọi trên lớp con. Nếu bạn vẫn chưa hiểu đầy đủ lý do tại sao mã này hoạt động, hãy viết lại nó bằng ngôn ngữ đơn giản:
Животное животное = new ДомашнееЖивотное();
Không có vấn đề gì với điều đó, phải không? Hãy tưởng tượng đây là cuộc sống thực và liên kết trong trường hợp này là một thẻ giấy đơn giản có nội dung “Động vật”. Nếu bạn lấy một mảnh giấy như vậy và gắn nó vào cổ áo của bất kỳ con vật cưng nào thì mọi chuyện sẽ ổn thôi. Thú cưng nào cũng vẫn là động vật! Quá trình ngược lại, tức là chuyển cây thừa kế xuống cho những người thừa kế, là một sự thu hẹp:
public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Như bạn có thể thấy, ở đây chúng tôi chỉ rõ lớp nào chúng tôi muốn truyền đối tượng của mình vào. Trước đây chúng ta có một biến WildAnimal, và bây giờ Coyote, biến này nằm trong cây kế thừa. Điều hợp lý là trình biên dịch sẽ không bỏ qua thao tác như vậy nếu không có dấu hiệu rõ ràng, nhưng nếu bạn chỉ định loại trong ngoặc đơn, mọi thứ sẽ hoạt động. Mở rộng và thu hẹp các loại tham chiếu - 2 Hãy xem một ví dụ khác, thú vị hơn:
public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal();//error!
   }
}
Trình biên dịch báo lỗi! Lý do là gì? Thực tế là bạn đang cố gán một đối tượng cha cho một biến con. Nói cách khác, bạn muốn làm điều này:
ДомашнееЖивотное домашнееЖивотное = new Животное();
Nhưng có lẽ nếu chúng ta chỉ ra rõ ràng loại mà chúng ta đang cố gắng hướng tới thì chúng ta sẽ thành công? Những con số có vẻ hiệu quả, hãy thử xem! :)
public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Ngoại lệ trong chuỗi "chính" java.lang.ClassCastException: Không thể chuyển động vật sang Lỗi thú cưng! Lần này trình biên dịch không phàn nàn, nhưng kết quả là chúng tôi nhận được một ngoại lệ. Chúng ta đã biết lý do: chúng ta đang cố gắng gán một đối tượng cha cho một biến con. Trên thực tế, tại sao điều này không thể thực hiện được? Bởi vì không phải tất cả Động vật đều là Thú cưng. Bạn đã tạo một đối tượng Animalvà đang cố gán nó cho một biến Pet. Nhưng, ví dụ, chó sói cũng là Animal, nhưng nó không phải Petlà vật nuôi. Nói cách khác, khi bạn viết:
Pet pet = (Pet) new Animal();
new Animal()Bất kỳ con vật nào cũng có thể ở đó và nó không nhất thiết phải là vật nuôi trong nhà! Đương nhiên, biến của bạn Pet petchỉ phù hợp để lưu trữ vật nuôi (và con cháu của chúng) chứ không phù hợp với tất cả mọi người. Do đó, đối với những trường hợp như vậy, một ngoại lệ đặc biệt đã được tạo trong Java - ClassCastExceptionmột lỗi khi truyền các lớp. Hãy nói lại lần nữa cho rõ ràng. Một biến cha (tham chiếu) có thể trỏ đến một đối tượng của lớp con:
public class Main {

   public static void main(String[] args) {

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

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Ví dụ: chúng tôi sẽ không gặp bất kỳ vấn đề nào ở đây. Chúng ta có một đối tượng Petđược trỏ tới bởi một liên kết Pet. Sau đó, một liên kết mới bắt đầu trỏ đến cùng một đối tượng Animal. Sau đó chúng tôi thực hiện chuyển đổi animalsang Pet. Nhân tiện, tại sao chúng tôi lại làm điều này? Lần trước chúng tôi có một ngoại lệ! Bởi vì lần này đối tượng ban đầu của chúng ta là Pet pet!
Pet pet =  new Pet();
Và trong ví dụ trước nó là một đối tượng Animal:
Pet pet = (Pet) new Animal();
Một biến con không thể được gán làm đối tượng tổ tiên. Ngược lại, bạn có thể làm được.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION