CodeGym /Blog Java /Ngẫu nhiên /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
CHÀO! Trong một bài học trước, chúng ta đã thảo luận về việc truyền các kiểu nguyên thủy. Hãy nhớ lại ngắn gọn những gì đã được thảo luận. Mở rộng và thu hẹp các loại tham chiếu - 1Chúng tôi đã tưởng tượng các kiểu nguyên thủy (trong trường hợp này là kiểu số) giống như những con búp bê lồng vào nhau có kích thước khác nhau tùy theo dung lượng bộ nhớ mà chúng chiếm giữ. Như bạn sẽ nhớ, việc đặt một con búp bê nhỏ hơn bên trong một con búp bê lớn hơn rất đơn giản cả trong đời thực và trong lập trình Java.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
Đây là một ví dụ về chuyển đổi hoặc mở rộng tự động . Nó tự xảy ra, vì vậy bạn không cần phải viết mã bổ sung. Cuối cùng, chúng tôi không làm điều gì bất thường: chúng tôi chỉ đặt một con búp bê nhỏ hơn vào một con búp bê lớn hơn. Đó là một vấn đề khác nếu chúng ta cố gắng làm điều ngược lại và đặt một con búp bê Nga lớn hơn vào một con búp bê nhỏ hơn. Bạn không thể làm điều đó trong cuộc sống thực, nhưng trong lập trình thì bạn có thể. Nhưng có một sắc thái. Nếu chúng ta cố gắng đặt một biến intvào một shortbiến, mọi thứ sẽ không suôn sẻ với chúng ta. Xét cho cùng, shortbiến chỉ chứa 16 bit thông tin, nhưng biến intchiếm 32 bit! Kết quả là, giá trị đã truyền bị biến dạng. Trình biên dịch sẽ báo lỗi cho chúng tôi (' Anh bạn, bạn đang làm điều gì đó đáng ngờ!'). Nhưng nếu chúng tôi chỉ ra rõ ràng loại mà chúng tôi đang chuyển đổi giá trị của mình thành, nó sẽ tiếp tục và thực hiện thao tác.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Đó chỉ là những gì chúng tôi đã làm trong ví dụ trên. Hoạt động đã được thực hiện, nhưng vì shortbiến chỉ có thể chứa 16 trong số 32 byte, nên giá trị cuối cùng bị biến dạng và chúng tôi nhận được số -27008 . Hoạt động như vậ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ờ hãy nói về các toán tử tương tự không được á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 ! Làm thế nào để điều này làm việc trong Java? Nó thực sự khá đơn giản. Có những đối tượng không liên quan. Sẽ là hợp lý khi cho rằng chúng không thể được chuyển đổi cho nhau, không rõ ràng hay tự động:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

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

   }

}
Ở đây, tất nhiên, chúng tôi nhận được một lỗi. Các lớp CatDogkhông liên quan đến nhau và chúng tôi chưa viết 'bộ chuyển đổi' để chuyển từ lớp này sang lớp khác. Điều hợp lý là 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 từ kiểu này sang kiểu khác. Nếu các đối tượng có liên quan, tốt, đó là một vấn đề khác! Liên quan như thế nào? Trên hết, thông qua thừa kế. Hãy thử sử dụng tính kế thừa để tạo một hệ thống nhỏ gồm các lớp. 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");
   }
}
Mọi người đều biết rằng động vật có thể được thuần hóa (thú cưng) hoặc 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ụ: lấy răng nanh - chúng tôi có 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");
   }
}
Chúng tôi đặc biệt chọn những lớp cơ bản nhất để dễ hiểu hơn. Chúng tôi không thực sự cần bất kỳ trường nào và một phương pháp là đủ. Hãy thử thực thi mã này:

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Bạn nghĩ gì sẽ được hiển thị trên bảng điều khiển? introducePhương thức của Petlớp hoặc lớp sẽ Animalđược gọi? Cố gắng biện minh cho câu trả lời của bạn trước khi bạn tiếp tục đọc. Và đây là kết quả! Tôi là thú cưng Tại sao chúng ta lại nhận được điều đó? Tất cả đều đơn giản. Chúng ta có một biến cha mẹ và một đối tượng con cháu. Bằng cách viết,

Animal animal = new Pet();
chúng tôi đã mở rộng một Pettham chiếu và gán nó cho một Animalbiến. Giống như các kiểu nguyên thủy, các kiểu tham chiếu được tự động mở rộng trong Java. Bạn không cần phải viết mã bổ sung để làm cho nó xảy ra. Bây giờ chúng ta có một đối tượng con được gán cho một tham chiếu gốc. Kết quả là, chúng ta thấy rằng lệnh gọi phương thức được thực hiện trên lớp con cháu. Nếu bạn vẫn chưa hoàn toàn hiểu tại sao mã này hoạt động, hãy viết lại bằng ngôn ngữ đơn giản:

Animal animal = new DomesticatedAnimal();
Không có vấn đề với điều này, phải không? Hãy tưởng tượng rằng đây là cuộc sống thực và tài liệu tham khảo chỉ đơn giản là một nhãn giấy có ghi 'Động vật' trên đó. Nếu bạn lấy mảnh giấy đó và gắn nó vào cổ áo của bất kỳ con vật cưng nào, mọi thứ sẽ chính xác. Rốt cuộc, bất kỳ thú cưng nào cũng là động vật! Quá trình ngược lại - di chuyển xuống cây thừa kế cho con cháu - đang 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ỉ ra rõ ràng lớp mà chúng tôi muốn chuyển đổi đối tượng của mình sang. Trước đây chúng tôi đã có một WildAnimalbiến và bây giờ chúng tôi có một Coyotebiến thấp hơn trên cây thừa kế. Có nghĩa là nếu không có dấu hiệu rõ ràng thì trình biên dịch sẽ không cho phép thao tác như vậy, nhưng nếu chúng ta chỉ định loại trong ngoặc đơn, thì mọi thứ sẽ hoạt động. Mở rộng và thu hẹp các loại tham chiếu - 2Hãy xem xét 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 tạo ra một lỗi! Nhưng tại sao? Bởi vì bạn đang cố gán một đối tượng cha cho một tham chiếu con. Nói cách khác, bạn đang cố gắng làm điều gì đó như thế này:

DomesticatedAnimal domesticatedAnimal = new Animal();
Chà, có lẽ mọi thứ sẽ hoạt động nếu chúng tôi chỉ định rõ ràng loại mà chúng tôi đang cố gắng chuyển đổi thành? Điều đó đã hiệu quả với các con số — Hãy thử xem! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Ngoại lệ trong luồng "chính" java.lang.ClassCastException: Không thể truyền động vật thành Lỗi thú cưng! Trình biên dịch không hét vào mặt chúng tôi lần này, nhưng chúng tôi đã kết thúc với một ngoại lệ. Chúng ta đã biết lý do: chúng ta đang cố gán một đối tượng cha cho một tham chiếu con. Nhưng tại sao chính xác bạn không thể làm điều đó? Bởi vì không phải tất cả Động vật đều là Động vật được thuần hóa. Bạn đã tạo một Animalđối tượng và đang cố gán nó cho một Petbiến. Một con sói đồng cỏ cũng là một Animal, nhưng nó không phải là một Pet. Nói cách khác, khi bạn viết

Pet pet = (Pet) new Animal();
new Animal()có thể đại diện cho bất kỳ con vật nào, không nhất thiết phải là thú cưng! Đương nhiên, Pet petbiến của bạn chỉ phù hợp để lưu trữ Thú cưng (và hậu duệ của chúng) chứ không phải bất kỳ loại động vật nào. Đó là lý do tại sao một ngoại lệ Java đặc biệt, ClassCastException, được tạo cho các trường hợp xảy ra lỗi khi truyền lớp. Hãy xem lại nó một lần nữa để làm cho mọi thứ rõ ràng hơn. Một tham chiếu cha có thể trỏ đến các thể hiện 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ụ, ở đây chúng tôi không có vấn đề gì. Chúng ta có một Petđối tượng được tham chiếu bởi một Petbiến. Sau đó, một Animaltài liệu tham khảo chỉ vào cùng một đối tượng. Sau đó, chúng tôi chuyển đổi animalthành tệp Pet. Nhân tiện, tại sao điều đó lại hiệu quả với chúng tôi? Lần trước chúng ta có một ngoại lệ! Bởi vì lần này đối tượng ban đầu của chúng ta là một Pet!

Pet pet = new Pet();
Nhưng trong ví dụ trước, nó là một Animalđối tượng:

Pet pet = (Pet) new Animal();
Bạn không thể gán một đối tượng tổ tiên cho một biến hậu duệ. Bạn có thể làm ngược lại.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION