2013-08-03 35 views
13
public class Test { 
    public static class Nested<T> { 
     public T val; 
     Nested(T val) { this.val = val; } 
    } 
    public static void main(String[] args) { 
     Nested<Integer> a = new Nested<Integer>(5); 
     Nested<Integer> b = new Nested<Integer>(2); 
     Integer diff = a.val - b.val; 
    } 
} 

Mã trên hoạt động tốt. Tuy nhiên, nếu tôi thêm một phương pháp để lồng nhau:Tại sao loại xóa của Java không phá vỡ điều này?

T diff(Nested<T> other) { return this.val - other.val; } 

tôi nhận được một lỗi biên dịch:

operator - cannot be applied to T,T 

Điều này làm cho ý nghĩa với tôi. Kiểu T bị xóa khi chạy, vì vậy Java không thể áp dụng toán tử chỉ được định nghĩa cho các lớp nhất định như Integer. Nhưng tại sao a.val - b.val hoạt động?

Chỉnh sửa:

Rất nhiều câu trả lời hay. Cảm ơn mọi người. Các ý chính của nó, nếu tôi hiểu chính xác, là trình biên dịch có thể thêm phôi vào số nguyên trong a.val - b.val vì nó biết ab được khởi tạo là Nested<Integer>. Tuy nhiên, kể từ khi this.val - other.val xảy ra bên trong cơ thể của một định nghĩa hàm chung (trong đó T vẫn có thể là bất cứ điều gì), trình biên dịch không thể thêm các phôi cần thiết để thực hiện công việc "-". Điều này dẫn đến một câu hỏi thú vị hơn, cụ thể là, nếu trình biên dịch Java có khả năng inlining, nó sẽ có thể cho một hàm chung như diff để làm việc?

+1

Kiểu 'a.val' được biết tại thời gian biên dịch thành' Số nguyên' trong trường hợp đầu tiên, nhưng chỉ 'T' trong trường hợp thứ hai. Trong trường hợp thứ hai, hãy xem xét điều gì xảy ra nếu một mô đun khác khởi tạo 'Test.Nested ' cho một số lớp 'Foo'. – nneonneo

+0

Cụ thể, trình biên dịch chèn một '(Integer)' cast ở phía trước của các biểu thức 'a.val' và' b.val'. Đây là những gì generics làm. –

Trả lời

9

Sự khác biệt giữa hai là liệu bạn đang ở trong một phương pháp chung hay bạn ở bên ngoài phương pháp chung.

Bạn hoàn toàn đúng rằng bên trong phương thức T không được biết là Integer, vì vậy toán tử trừ đi - không thể áp dụng được. Tuy nhiên, khi bạn đang ở trong main(), bên ngoài phương pháp chung, trình biên dịch biết rằng bạn đã khởi tạo Nested với Integer, do đó, nó biết rất rõ cách áp dụng toán tử. Mặc dù việc thực hiện chung đã xóa loại để tạo mã cho Nested<T>, trình biên dịch không nghĩ về ab về mặt số Nested<T>: nó có đủ kiến ​​thức để chèn một dàn diễn viên phù hợp, bỏ kết quả và áp dụng dấu trừ - nhà điều hành.

1

a.val - b.val hoạt động vì nó được trình biên dịch xác thực, chứ không phải trong thời gian chạy. Trình biên dịch "thấy" rằng bạn đang sử dụng <Integer> và nó biên dịch và chạy Ok, trong thời gian chạy không có vấn đề gì ngay cả khi xóa vì trình biên dịch đã xác thực điều đó.

1

Vì cuộc gọi phương thức là lúc chạy và a.val - b.val được chọn tại thời gian biên dịch.

  • Trong trường hợp đầu tiên, trình biên dịch biết rằng loại là Integer- hoạt động được phép cho số nguyên.
  • Trong trường hợp thứ hai, loại T không được biết đến với trình biên dịch trước, do đó nó không chắc liệu hoạt động - có hợp lệ hay không. Do đó lỗi trình biên dịch.

Hãy xem xét chúng tôi sử dụng phương pháp này là diff(Nested<Book> other) để không có cách nào có thể trừ một cuốn sách từ người khác.

8

Bạn đang gặp phải lỗi biên dịch, không phải là lỗi thời gian chạy.

public static void main(String[] args) { 
    Nested<Integer> a = new Nested<Integer>(5); 
    Nested<Integer> b = new Nested<Integer>(2); 
    Integer diff = a.val - b.val; 
} 

Ở đây, biên dịch biết rằng cả hai TInteger. Bạn vừa khai báo <Integer>.

T diff(Nested<T> other) { return this.val - other.val; } 

Ở đây, trình biên dịch không chắc chắn về T. Nó có thể là bất cứ điều gì. Và, toán tử số duy nhất - không được phép cho bất kỳ thứ gì.

1

Vì mã không tồn tại trong Nested, loại này được biết. Trình biên dịch có thể thấy rõ ràng rằng a.val - b.val là một số nguyên trừ đi một số nguyên, có thể được tự động đóng hộp. Trình biên dịch về cơ bản viết lại nó để

Integer diff = Integer.valueOf(((Integer) a.val).intValue() - ((Integer) b.val).intValue()) 

các .intValue và .valueOf các cuộc gọi đến từ các auto-boxing và unboxing tự động.

Loại phôi được an toàn để trình biên dịch chèn vào vì bạn đã sử dụng loại tham số được lồng vào nhau.

Đúng, về mặt kỹ thuật, một số có thể là một thứ khác, như đối tượng Lịch, vì loại này không xác định khi chạy. Nhưng nếu bạn đang sử dụng Generics, trình biên dịch tin tưởng rằng bạn không làm bất cứ điều gì câm để phá vỡ nó. Do đó, nếu a.val hoặc b.val là bất kỳ thứ gì khác với Số nguyên, thì một ClassCastException sẽ được ném vào thời gian chạy.

Các vấn đề liên quan