1. Đánh máy

Các biến lưu trữ các loại tham chiếu (các lớp) cũng có thể được chuyển đổi thành các loại khác nhau. Nhưng điều này chỉ hoạt động trong một hệ thống phân cấp loại duy nhất. Hãy xem xét một ví dụ đơn giản. Giả sử chúng ta có hệ thống phân cấp lớp sau, trong đó các lớp bên dưới kế thừa các lớp bên trên.

đánh máy

Việc đánh máy các kiểu tham chiếu cũng như kiểu nguyên thủy cũng được phân loại thành mở rộng và thu hẹp.

Chúng ta thấy rằng lớp Cat kế thừa lớp Pet và lớp Pet kế thừa lớp Animal.

Nếu chúng ta viết mã như thế này:

Animal kitten = new Cat();

Đây là một loại chuyển đổi mở rộng . Nó cũng được gọi là một diễn viên tiềm ẩn. Chúng tôi đã mở rộng tham chiếu mèo để bây giờ nó đề cập đến một đối tượng Cat . Với việc chuyển đổi kiểu như thế này, chúng ta sẽ không thể sử dụng tham chiếu kitten để gọi các phương thức có trong lớp Cat nhưng không có trong lớp Animal .

Chuyển đổi thu hẹp (hoặc chuyển đổi rõ ràng) xảy ra theo hướng ngược lại:

Cat cat = (Cat) kitten;

Chúng tôi đã chỉ ra rõ ràng rằng chúng tôi muốn truyền tham chiếu được lưu trữ trong biến mèo con (có loại là Animal ) sang loại Cat .



2. Kiểm tra loại đối tượng

Nhưng bạn cần phải rất cẩn thận ở đây. Nếu bạn làm điều này:

Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;

Trình biên dịch sẽ cho phép mã này, nhưng sẽ có lỗi khi chương trình chạy! JVM sẽ đưa ra một ngoại lệ:

Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf

Các tham chiếu đến một đối tượng Cat chỉ có thể được lưu trữ trong các biến có kiểu là tổ tiên của lớp Cat: Pet, Animal hoặc Object.

Tại sao vậy?

Điểm liên quan ở đây là một tham chiếu đối tượng được sử dụng để chỉ các phương thức và biến của đối tượng đó . Và sẽ không có vấn đề gì nếu chúng ta sử dụng biến Animal để lưu trữ tham chiếu đến đối tượng Cat: kiểu Cat luôn có các biến và phương thức của kiểu Animal — nó kế thừa chúng!

Nhưng nếu JVM cho phép chúng ta lưu trữ một tham chiếu đến một đối tượng Cat trong một biến Wolf, thì chúng ta có thể gặp tình huống cố gắng sử dụng biến greyWolf để gọi một phương thức không tồn tại trong đối tượng Cat được lưu trữ trong biến đó . Đó là lý do tại sao sự sắp xếp này không được phép.

Java có một instanceoftoán tử đặc biệt cho phép bạn kiểm tra xem một đối tượng có thuộc một loại nhất định hay không và do đó có thể được lưu trữ vào một biến thuộc một loại nhất định. Nó trông khá đơn giản:

variable instanceof Type

Ví dụ:

Animal beast = new Cat();
if (beast instanceof Wolf)
{
   Wolf grayWolf = (Wolf) beast;
}

Mã này sẽ không gây ra lỗi — ngay cả trong thời gian chạy.

Dưới đây là một số ví dụ khác minh họa tình huống:

Mở rộng loại chuyển đổi Sự miêu tả
Cow cow = new Whale();

Đây là một chuyển đổi mở rộng cổ điển — không yêu cầu toán tử chuyển đổi loại. Bây giờ chỉ các phương thức được định nghĩa trong Cowlớp mới có thể được gọi trên Whaleđối tượng.

Trên cowbiến , trình biên dịch sẽ chỉ cho phép bạn gọi các phương thức mà kiểu của nó ( Cowlớp) có.

Thu hẹp loại chuyển đổi
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Chuyển đổi thu hẹp cổ điển: Bạn cần thêm kiểm tra loại và toán tử truyền.
Biến Cow cowlưu trữ một tham chiếu đến một Whaleđối tượng.
Chúng tôi xác minh rằng đây là trường hợp và sau đó thực hiện chuyển đổi loại (thu hẹp). Hay như nó còn được gọi là:
một loại diễn viên
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // Exception
Bạn có thể thu hẹp loại tham chiếu mà không cần kiểm tra loại đối tượng.
Nếu cowbiến đề cập đến một đối tượng không phải là a Whale, thì an InvalidClassCastExceptionsẽ được tạo.


3. Gọi phương thức ban đầu: từ superkhóa

Khi ghi đè phương thức của lớp cha, đôi khi thay vì thay thế nó bằng phương thức của chúng ta, chúng ta chỉ muốn bổ sung nó một chút.

Sẽ thật tuyệt nếu chúng ta có thể sử dụng phương thức của lớp cha trong phương thức của mình và sau đó thực thi một số mã của riêng chúng ta. Hoặc có lẽ trước tiên hãy thực thi mã của chính chúng ta, sau đó gọi phương thức của lớp cha.

Và Java cho phép chúng ta làm điều đó. Để gọi một phương thức của lớp cha, hãy làm như sau:

super.method(arguments);

Ví dụ:

class PeaceTime
{
   public double getPi()
   {
      return 3.14;
   }
}

class WarTime extends PeaceTime
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

Trong thời chiến, giá trị của Picó thể lớn hơn 6! Tất nhiên, chúng tôi đang nói đùa, nhưng ví dụ này cho thấy tất cả điều này có thể hoạt động như thế nào.

Dưới đây là một vài ví dụ để làm rõ mọi thứ một chút:

Mã số Sự miêu tả
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("This is incorrect: ");
      super.printName();
      System.out.println("I'm a whale");
   }
}
CowWhalecác lớp học
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
Đầu ra màn hình sẽ là:
I'm a white whale
This is incorrect: I'm a cow
I'm a whale

Đây là thứ khó. Thành thật mà nói, đó là một trong những điều khó nhất trong OOP . Điều đó nói rằng, bạn cần phải biết và hiểu nó.