Cần có một số ngữ cảnh để hiểu đầy đủ lý do chính đằng sau điều này.
Nguyên thủy so với các lớp
Các biến số nguyên trong Java chứa các giá trị (số nguyên, số nhị phân dấu phẩy động có độ chính xác kép, v.v.). Bởi vì các giá trị này có thể có độ dài khác nhau , các biến chứa chúng cũng có thể có độ dài khác nhau (xem xét float
so với double
).
Mặt khác, các biến lớp chứa các tham chiếu đến các thể hiện. Tham chiếu thường được triển khai dưới dạng con trỏ (hoặc thứ gì đó rất giống với con trỏ) trong nhiều ngôn ngữ. Những điều này thường có cùng kích thước, không phụ thuộc vào kích thước của các trường hợp họ tham khảo ( Object
, String
, Integer
, vv).
Thuộc tính này của các biến lớp làm cho các tham chiếu mà chúng chứa có thể hoán đổi cho nhau (ở một mức độ nào đó). Điều này cho phép chúng tôi thực hiện những gì chúng tôi gọi là thay thế : nói một cách rộng rãi, sử dụng một thể hiện của một kiểu cụ thể như một thể hiện của một kiểu liên quan khác ( ví dụ: sử dụng một String
như một Object
).
Các biến nguyên thủy không thể hoán đổi cho nhau theo cùng một cách, không với nhau hoặc với Object
. Lý do rõ ràng nhất cho điều này (nhưng không phải là lý do duy nhất) là sự khác biệt về kích thước của chúng. Điều này làm cho các kiểu nguyên thủy trở nên bất tiện về mặt này, nhưng chúng tôi vẫn cần chúng trong ngôn ngữ (vì những lý do chủ yếu là giảm hiệu suất).
Generics và loại xóa
Loại chung là loại có một hoặc nhiều tham số loại (số chính xác được gọi là độ hiếm chung ). Ví dụ, định nghĩa kiểu chung List<T>
có một tham số kiểu T
, có thể là Object
(tạo ra một kiểu cụ thể List<Object>
), String
( List<String>
), Integer
( List<Integer>
), v.v.
Loại chung phức tạp hơn rất nhiều so với loại không chung. Khi chúng được giới thiệu với Java (sau khi phát hành lần đầu), để tránh thực hiện các thay đổi triệt để đối với JVM và có thể phá vỡ khả năng tương thích với các tệp nhị phân cũ hơn, những người tạo ra Java đã quyết định triển khai các kiểu chung theo cách ít xâm lấn nhất: tất cả các kiểu cụ thể của List<T>
trên thực tế, được biên dịch thành (tương đương nhị phân của) List<Object>
(đối với các loại khác, ràng buộc có thể là một cái gì đó khác hơn Object
, nhưng bạn hiểu rõ). Thông tin tham số kiểu và độ hiếm chung chung bị mất trong quá trình này , đó là lý do tại sao chúng tôi gọi nó là xóa kiểu .
Gắn kết cả hai lại với nhau
Bây giờ vấn đề là sự kết hợp của các thực tế trên: nếu List<T>
trở thành List<Object>
trong mọi trường hợp, thì T
luôn phải là một kiểu có thể được gán trực tiếp choObject
. Bất cứ điều gì khác không được phép. Kể từ đó, như chúng tôi đã nói trước đây, int
, float
và double
không được hoán đổi với Object
, có thể không phải là một List<int>
, List<float>
hoặc List<double>
(trừ khi thực hiện đáng kể phức tạp hơn của Generics tồn tại trong JVM).
Nhưng Mời Java loại thích Integer
, Float
và Double
đó quấn những nguyên thủy trong trường lớp, làm cho chúng có hiệu quả thể thay thế như Object
, do đó cho phép các loại tổng quát để gián tiếp làm việc với các nguyên thủy cũng (vì bạn có thể có List<Integer>
, List<Float>
, List<Double>
và vân vân).
Quá trình tạo ra một Integer
từ an int
, a Float
từ a float
, v.v., được gọi là quyền anh . Ngược lại được gọi là mở hộp . Bởi vì việc phải đóng hộp các nguyên bản mỗi khi bạn muốn sử dụng chúng Object
là điều bất tiện, nên có những trường hợp ngôn ngữ thực hiện điều này tự động - được gọi là autoboxing .