Tự động Downcasting bằng cách suy ra Loại


11

Trong java, bạn phải truyền một cách rõ ràng để hạ thấp một biến

public class Fruit{}  // parent class
public class Apple extends Fruit{}  // child class

public static void main(String args[]) {
    // An implicit upcast
    Fruit parent = new Apple();
    // An explicit downcast to Apple
    Apple child = (Apple)parent;
}

Có bất kỳ lý do cho yêu cầu này, ngoài thực tế là java không thực hiện bất kỳ loại suy luận nào?

Có "gotchas" nào với việc thực hiện downcasting tự động trong một ngôn ngữ mới không?

Ví dụ:

Apple child = parent; // no cast required 

Bạn đang nói rằng, nếu trình biên dịch có thể suy ra rằng đối tượng bị downcast luôn thuộc loại chính xác, bạn có thể không thể hiện rõ ràng downcast không? Nhưng sau đó tùy thuộc vào phiên bản trình biên dịch và mức độ suy luận của nó, một số chương trình có thể không hợp lệ ... các lỗi trong loại suy luận có thể ngăn các chương trình hợp lệ được viết, v.v ... không có ý nghĩa gì để giới thiệu một số đặc biệt trường hợp phụ thuộc vào rất nhiều yếu tố, nó sẽ không trực quan với người dùng nếu họ phải tiếp tục thêm hoặc xóa phôi mà không có lý do tại mỗi bản phát hành.
Bakuriu

Câu trả lời:


24

Upcasts luôn thành công.

Truyền phát có thể dẫn đến lỗi thời gian chạy, khi kiểu thời gian chạy đối tượng không phải là kiểu con của kiểu được sử dụng trong truyền.

Vì thứ hai là một hoạt động nguy hiểm, hầu hết các ngôn ngữ lập trình được gõ yêu cầu lập trình viên yêu cầu rõ ràng về nó. Về cơ bản, lập trình viên đang nói với trình biên dịch "hãy tin tôi, tôi biết rõ hơn - điều này sẽ ổn khi chạy".

Khi các hệ thống loại được quan tâm, các chương trình phát sóng đặt gánh nặng của bằng chứng lên trình biên dịch (phải kiểm tra nó một cách tĩnh), các chương trình phát sóng đặt gánh nặng của bằng chứng lên lập trình viên (phải suy nghĩ kỹ về nó).

Người ta có thể lập luận rằng một ngôn ngữ lập trình được thiết kế đúng sẽ cấm hoàn toàn truyền phát hoặc cung cấp các lựa chọn thay thế diễn viên an toàn, ví dụ như trả về một loại tùy chọn Option<T>. Tuy nhiên, nhiều ngôn ngữ phổ biến đã chọn cách tiếp cận đơn giản và thực tế hơn chỉ đơn giản là trả lại Tvà đưa ra một lỗi khác.

Trong ví dụ cụ thể của bạn, trình biên dịch có thể đã được thiết kế để suy ra đó parentthực sự là Applemột phân tích tĩnh đơn giản và cho phép truyền diễn viên ẩn. Tuy nhiên, nói chung, vấn đề là không thể giải quyết được, vì vậy chúng tôi không thể mong đợi trình biên dịch thực hiện quá nhiều phép thuật.


1
Chỉ để tham khảo, một ví dụ về ngôn ngữ phát xuống các tùy chọn là Rust, nhưng đó là vì nó không có sự kế thừa thực sự, chỉ có "bất kỳ loại nào" .
Kroltan

3

Thông thường downcasting là những gì bạn làm khi kiến ​​thức được biết đến tĩnh mà trình biên dịch có về loại của một cái gì đó ít cụ thể hơn những gì bạn biết (hoặc ít nhất là hy vọng).

Trong các tình huống như ví dụ của bạn, đối tượng được tạo ra như một Applekiến thức và sau đó kiến ​​thức đó bị loại bỏ bằng cách lưu trữ giới thiệu trong một biến loại Fruit. Sau đó, bạn muốn sử dụng cùng một giới thiệu như một Applelần nữa.

Vì thông tin chỉ bị vứt bỏ "cục bộ", chắc chắn, trình biên dịch có thể duy trì kiến ​​thức parentthực sự là một Apple, mặc dù kiểu khai báo của nó là Fruit.

Nhưng thường thì không ai làm điều đó. Nếu bạn muốn tạo Applevà sử dụng nó như một Apple, bạn lưu nó trong một Applebiến chứ không phải một biến Fruit.

Khi bạn có Fruitvà muốn sử dụng nó như một cách Applethường có nghĩa là bạn đã có được Fruitthông qua một số phương tiện thường có thể trả về bất kỳ loại nào Fruit, nhưng trong trường hợp này bạn biết đó là một Apple. Hầu như bạn luôn không xây dựng nó, bạn đã được thông qua nó bởi một số mã khác.

Một ví dụ rõ ràng là nếu tôi có một parseFruithàm có thể biến các chuỗi như "apple", "cam", "Lemon", v.v., thành lớp con thích hợp; nói chung tất cả những gì chúng ta (và trình biên dịch) có thể biết về hàm này là nó trả về một số loại Fruit, nhưng nếu tôi gọi parseFruit("apple")thì tôi biết rằng nó sẽ gọi Applevà có thể muốn sử dụng Applecác phương thức, vì vậy tôi có thể downcast.

Một lần nữa, một trình biên dịch đủ thông minh có thể tìm ra điều đó ở đây bằng cách nội tuyến mã nguồn parseFruit, vì tôi gọi nó bằng một hằng số (trừ khi nó nằm trong một mô-đun khác và chúng tôi có trình biên dịch riêng biệt, như trong Java). Nhưng bạn sẽ dễ dàng có thể thấy các ví dụ phức tạp hơn liên quan đến thông tin động có thể trở nên khó khăn hơn (hoặc thậm chí là không thể!) Để trình biên dịch xác minh.

Trong các chương trình truyền phát mã thực tế thường xảy ra khi trình biên dịch không thể xác minh rằng chương trình truyền phát an toàn bằng các phương thức chung và không phải trong các trường hợp đơn giản như ngay lập tức sau khi một chương trình phát sóng bị loại bỏ thông tin cùng loại mà chúng tôi đang cố gắng lấy lại bằng cách truyền phát.


3

Đó là vấn đề bạn muốn vẽ đường thẳng ở đâu. Bạn có thể thiết kế một ngôn ngữ sẽ phát hiện tính hợp lệ của downcast ngầm:

public static void main(String args[]) { 
    // An implicit upcast 
    Fruit parent = new Apple();
    // An implicit downcast to Apple 
    Apple child = parent; 
}

Bây giờ, hãy trích xuất một phương thức:

public static void main(String args[]) { 
    // An implicit upcast 
    Fruit parent = new Apple();
    eat(parent);
}
public static void eat(Fruit parent) { 
    // An implicit downcast to Apple 
    Apple child = parent; 
}

Chúng tôi vẫn tốt. Phân tích tĩnh khó hơn nhiều nhưng vẫn có thể.

Nhưng vấn đề xuất hiện ngay giây phút ai đó thêm vào:

public static void causeTrouble() { 
    // An implicit upcast 
    Fruit trouble = new Orange();
    eat(trouble);
}

Bây giờ, bạn muốn nâng lỗi ở đâu? Điều này tạo ra một vấn đề nan giải, người ta có thể nói rằng vấn đề đang xảy ra Apple child = parent;, nhưng điều này có thể bị bác bỏ bởi "Nhưng nó đã hoạt động trước đây". Mặt khác, việc thêm eat(trouble);gây ra vấn đề ", nhưng toàn bộ quan điểm của đa hình là cho phép chính xác điều đó.

Trong trường hợp này, bạn có thể thực hiện một số công việc của lập trình viên, nhưng bạn không thể làm tất cả mọi cách. Bạn càng nhận được nó trước khi từ bỏ, sẽ càng khó để giải thích những gì đã sai. Vì vậy, tốt hơn hết là tạm dừng càng sớm càng tốt, theo nguyên tắc báo cáo lỗi sớm.

BTW, trong Java, downcast mà bạn mô tả không thực sự là một downcast. Đây là một diễn viên nói chung, có thể đúc táo cũng như cam. Vì vậy, về mặt kỹ thuật, ý tưởng của @ chi đã có ở đây, không có chương trình phát sóng nào trong Java, chỉ có "mỗi lần phát". Sẽ rất hợp lý khi thiết kế một toán tử downcast chuyên dụng, nó sẽ đưa ra lỗi biên dịch khi loại kết quả không thể tìm thấy ở phía dưới từ kiểu đối số của nó. Sẽ là một ý tưởng tốt để làm cho "everycast" khó sử dụng hơn nhiều để ngăn cản các lập trình viên sử dụng nó mà không có lý do chính đáng. XXXXX_cast<type>(argument)Cú pháp của C ++ xuất hiện trong tâm trí.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.