Generics và Type-erasure


10

Generics trong Java được thực hiện bằng cách sử dụng kiểu xóa. JLS nói rằng cảm hứng là khả năng tương thích ngược. Trường hợp như mặt khác, C # generic có thể được xác nhận lại.

Về mặt lý thuyết những lợi thế và bất lợi của việc có Generics là "xóa" hoặc "có thể tái sử dụng" là gì?

Là Java thiếu vào một cái gì đó?

Câu trả lời:


8

Vâng, động lực (tương thích ngược) là cả một lợi thế và bất lợi. Đó là bất lợi bởi vì tất cả chúng ta muốn có các loại có thể xác nhận lại, nhưng giá phải trả là cao. Hãy xem xét các lựa chọn thiết kế trong C #. Chúng có các loại có thể xác nhận lại, nhưng bây giờ chúng có các API trùng lặp. Vì vậy, hãy tưởng tượng một API Java nơi chúng tôi cũng có các API trùng lặp cho mỗi lớp được tham số hóa. Bây giờ, hãy hình dung bản thân bạn chuyển hàng ngàn dòng mã từ các lớp kế thừa sang các lớp chung mới. Bây giờ, ai sẽ không coi các API trùng lặp là một bất lợi? Nhưng này, họ có loại đáng tin cậy!

Vì vậy, động lực chính là "tiến hóa, không phải cách mạng". Và theo logic, mọi quyết định đều có sự đánh đổi.

Bên cạnh những nhược điểm khác được đề cập, chúng ta cũng có thể thêm một thực tế là việc xóa kiểu có thể khó giải thích về thời gian biên dịch, vì không rõ ràng rằng một số loại sẽ bị xóa và điều này dẫn đến những lỗi rất kỳ lạ và khó tìm.

Sự tồn tại của các phương thức cầu nối (trình biên dịch này được tạo ra theo phương pháp cú pháp để giữ tính tương thích nhị phân) cũng có thể được coi là bất lợi. Và đây có thể chính xác là một trong những lý do của lỗi tôi đã đề cập trong đoạn trước.

Những bất lợi lớn xuất phát từ thực tế đã rõ ràng là có một lớp duy nhất và không có nhiều lớp cho các loại chung. Như một ví dụ khác xem xét rằng quá tải một phương thức có cùng lớp chung không thành công trong Java:

public void doSomething(List<One>);
public void doSomething(List<Two>);

Một cái gì đó có thể được coi là một bất lợi của các loại có thể xác nhận lại (ít nhất là trong C #) là thực tế rằng chúng gây ra vụ nổ mã . Ví dụ List<int>là một lớp và một lớp List<double>khác hoàn toàn khác, vì nó là a List<string>và a List<MyType>. Vì vậy, các lớp phải được xác định trong thời gian chạy, gây ra sự bùng nổ của các lớp và tiêu thụ tài nguyên có giá trị trong khi chúng đang được tạo.

Về thực tế là không thể định nghĩa một new T()trong Java, được đề cập trong một câu trả lời khác, cũng rất thú vị khi xem xét rằng đây không chỉ là vấn đề xóa kiểu. Nó cũng yêu cầu sự tồn tại của một hàm tạo mặc định, đó là lý do tại sao C # yêu cầu "ràng buộc mới" cho việc này. (Xem Tại sao T () mới không thể có trong Java , bởi Alex Buckley).


3
Tôi nghĩ rằng tôi đúng khi nói rằng trên CLR, đối với loại tham chiếu T , bạn chỉ nhận được một bản sao của Class<T>mã cho tất cả các Ts; cộng với một bản sao bổ sung cho từng loại giá trị T thực sự được sử dụng.
AakashM

@AakashM: Chính xác, có một loại Loại tham chiếu duy nhất cho mỗi loại chung, tuy nhiên mỗi Loại giá trị tạo phiên bản riêng. Điều này là do cung cấp không gian lưu trữ chuyên biệt dựa trên loại, tất cả các tham chiếu có cùng dung lượng lưu trữ nhưng các loại giá trị có dung lượng lưu trữ khác nhau cho mỗi loại.
Guvante

6

Nhược điểm của loại tẩy là bạn không biết trong thời gian chạy loại chung. Điều này ngụ ý rằng bạn không thể áp dụng sự phản chiếu lên chúng và bạn không thể khởi tạo chúng khi chạy.

Bạn không thể làm một cái gì đó như thế này trong Java:

public class MyClass<T> {
    private T t;

    //more methods    
    public void myMethod() {
        //more code
        if (someCondition) {
            t = new T();//illegal
        } else {
            T[] array = new T[];//illegal
        }            
    }       
}

Có một cách giải quyết cho việc này nhưng nó đòi hỏi nhiều mã hơn. Ưu điểm, như bạn đã đề cập, là khả năng tương thích ngược.


3

Một ưu điểm khác của các tổng quát đã bị xóa là các ngôn ngữ khác nhau được biên dịch cho JVM sử dụng các chiến lược khác nhau cho các tổng quát, ví dụ: trang định nghĩa của Scala so với hiệp phương sai sử dụng của Java. Ngoài ra, các loại thuốc generic loại cao hơn của Scala sẽ khó hỗ trợ hơn cho các loại hợp nhất của .Net, vì về cơ bản Scala trên .Net đã bỏ qua định dạng thống nhất không tương thích của C #. Nếu chúng ta đã thống nhất các tướng trong JVM, rất có thể các tướng được thống nhất đó sẽ không phù hợp với các tính năng mà chúng ta thực sự thích về Scala và chúng ta sẽ bị mắc kẹt với thứ gì đó không tối ưu. Trích dẫn từ blog của Ola Bini ,

Tất cả điều này có nghĩa là nếu bạn muốn thêm các tổng quát hợp nhất vào JVM, bạn nên chắc chắn rằng việc triển khai đó có thể bao gồm cả các ngôn ngữ tĩnh muốn thực hiện đổi mới trong phiên bản chung của riêng chúng và tất cả các ngôn ngữ động muốn tạo ra một triển khai tốt và một cơ sở giao tiếp tốt với các thư viện Java. Bởi vì nếu bạn thêm các tổng quát hợp nhất không đáp ứng các tiêu chí này, bạn sẽ kìm hãm sự đổi mới và khiến việc sử dụng JVM như một VM đa ngôn ngữ trở nên khó khăn hơn nhiều.

Cá nhân tôi không coi sự cần thiết phải sử dụng TypeTagbối cảnh bị ràng buộc trong Scala đối với các phương thức quá tải mâu thuẫn là một bất lợi, bởi vì nó chuyển chi phí và tính không linh hoạt của việc thống nhất từ ​​toàn cầu (toàn bộ chương trình và tất cả các ngôn ngữ có thể) sang sử dụng cho mỗi ngôn ngữ vấn đề chỉ là trường hợp ít thường xuyên hơn.



Một lý do khác để ủng hộ các thế hệ bị xóa là vì các phụ thuộc nên được đưa vào theo cách tôn trọng giải pháp cho Vấn đề Biểu hiện. Nếu bạn đang kiểm tra các trường hợp cụ thể về loại hoặc mã hóa một nhà máy trong chức năng chung của bạn, thì bạn đang làm sai khả năng mở rộng.
Shelby Moore III
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.