2016-09-14 20 views
6

Hãy tưởng tượng chúng ta có lớp chung sau đây:tham số kiểu Unspecified khi gọi phương pháp chung

public class GenericClass<U> { 
    private U value; 

    public GenericClass(U value) { 
     this.value = value; 
    } 
} 

và phương pháp chung sau đây trong một số lớp MyClass khác:

public <T extends BT, BT> void genericMethod(T arg) { 
    Object genericClass = new GenericClass<BT>(arg); 
} 

Những giá trị sẽ loại tham số BT có được nếu chúng tôi gọi

genericMethod("text"); 

?

Một số lưu ý:

Đoạn mã trên biên dịch mà không có lỗi hoặc cảnh báo, mà dường như xa lạ đối với tôi. Decompiling (bằng phương tiện của IntelliJ IDEA 2016) cho thấy đoạn mã sau:

public <T extends BT, BT> void genericMethod(T arg) { 
    new MyClass.GenericClass(arg); 
} 

ý rằng mới GenericClass<BT>(arg) là không giống như new GenericClass(arg) vì sau này là tương đương với new GenericClass<T>(arg) (loại trừ), và mặc dù T extends BT, đây là những các loại khác nhauGenericClass có thể có logic nội tại, trong đó tên loại chính xác đóng vai trò quan trọng (ví dụ: được sử dụng làm khóa chuỗi trong một số bản đồ, v.v.). Vì vậy, đối với tôi nó là lạ tại sao trình biên dịch âm thầm sử dụng loại khấu trừ thay vì sản xuất một số cảnh báo (hoặc thậm chí có thể lỗi) nói rằng tham số loại BT không được chỉ định. Có lẽ tôi đang mất tích. quan trọng về Generics trong Java, tuy nhiên ...

Trả lời

3

Phần đầu của câu trả lời này sẽ đề cập đến một phần suy luận kiểu của câu hỏi của bạn

Các nơi duy nhất một loại được tự động suy ra trong ví dụ của bạn khi gọi genericMethod.

Định nghĩa của genericMethod khai báo hai thông số loại chung: an unbound BTT which should be a subclass of BT.

Loại T sẽ được suy ra theo các quy tắc quy định tại Java language specification section 18.2.4:

Một công thức hạn chế của mẫu , trong đó S và T là các loại, giảm như sau:

...

Nếu không, nếu T là biến suy luận, α và S không phải là kiểu nguyên thủy, ràng buộc sẽ giảm xuống S = α bị ràng buộc.

Trong ví dụ của bạn, bạn cung cấp đối tượng kiểu String làm đối số loại T. Theo quy tắc trên, T sẽ được suy ra bằng String.

Bây giờ, T được khắc phục, suy luận sẽ tiếp tục với BT.loại này được suy ra bằng cách làm theo section 18.3.1 mà nói rằng một biểu hiện của loại:

T extends BT, T == String

ngụ ý

String extends BT

mối quan hệ này cho phép trình biên dịch để ràng buộc BT-String, do đó bạn kết thúc với cuộc gọi ngụ ý sau đây:

genericMethod<String,String>("text") 

Hy vọng điều này sẽ xóa mọi thứ một chút.

Để trả lời phần thứ hai của câu hỏi của bạn:

và mặc dù T kéo dài BT, đây là những loại khác nhau, và GenericClass có nội Logic nơi tên loại chính xác đóng vai trò quan trọng (ví dụ: được sử dụng như một khóa chuỗi trong một số bản đồ, vv).

Nếu không có thêm khó khăn hơn, trình biên dịch sẽ đối xử với T như tương đương với BT và sẽ cho phép bạn gọi chỉ các phương pháp định nghĩa như là một phần của các loại BT.

Trừ khi bạn đang làm điều gì đó rất lạ bằng cách sử dụng sự phản chiếu, mã không nên phụ thuộc vào giá trị thời gian chạy của đối số kiểu. Hiện tại GenericClass vì nó đứng không phải là rất hữu ích bởi vì U không có bất kỳ ràng buộc để trình biên dịch sẽ cho phép bạn làm chỉ những thứ có giá trị cho tất cả các đối tượng. Bạn có thể xem xét U trong ngữ cảnh đó giống như Object.

+0

Cảm ơn bạn đã trả lời. Biến "suy luận" là gì? Bạn có thể chỉ ra điều đó trong mẫu mã của tôi không? Trong khai báo genericMethod() tôi chỉ thấy hai biến * * (T và BT) và một biến thông thường (arg). Tôi đã đọc trong Chương 18.1.1 những điều sau đây: _ "Biến suy luận là siêu biến cho các loại - nghĩa là chúng là các tên đặc biệt cho phép lý luận trừu tượng về các loại. Để phân biệt chúng với các biến kiểu, các biến suy luận được biểu diễn với chữ cái Hy Lạp, chủ yếu là α "_. Tuy nhiên, nó không làm rõ thuật ngữ này cho tôi. –

+0

** Bạn viết: ** _String mở rộng BT Mối quan hệ này cho phép trình biên dịch liên kết BT thành chuỗi, do đó bạn kết thúc cuộc gọi ngụ ý sau: genericMethod ("text") _ - Tôi không thể hiểu tại sao nó cho phép làm điều này: "String extends BT" không giống như "BT == String"! ** Bạn cũng viết: ** _Không thêm nhiều ràng buộc, trình biên dịch sẽ xử lý T tương đương với BT và sẽ cho phép bạn chỉ gọi các phương thức được định nghĩa là một phần của loại BT._ - giống nhau ở đây! Trình biên dịch tốt hơn sẽ tạo ra một lỗi mà BT không được sử dụng ở bất cứ đâu. –

+0

** Bạn viết: ** _The GenericClass hiện tại vì nó là viết tắt không phải là rất hữu ích [...]. Bạn có thể xem xét U trong bối cảnh đó giống như Object._ - Tôi đồng ý: mẫu này là một mẫu quá chưng cất. Trong dự án thực sự của tôi, _JAXBElement_ đứng ở đây (thay cho GenericClass). –

1

Dựa trên phần này của câu hỏi của bạn

GenericClass thể có logic nội bộ nơi tên loại chính xác đóng vai trò quan trọng (ví dụ được sử dụng như một chìa khóa chuỗi trong một số bản đồ vv).

Tôi nghĩ có thể có một số nhầm lẫn, về cách các loại này được suy ra. Điều quan trọng là nhận ra rằng loại GenericClass sẽ chứa là kiểu thời gian chạy của bất kỳ thứ gì bạn truyền vào. Bất kỳ điều gì phụ thuộc vào loại chính xác, bạn có thể đang làm bên trong (mặc dù nói chung bạn thực sự không nên) sẽ hoạt động tốt .

Nhìn chung, việc kiểm tra kiểu được thực hiện tại thời gian biên dịch có vẻ hơi lỏng lẻo đối với bạn (đặc biệt nếu bạn đến từ C++ chẳng hạn). Có một cuộc thảo luận thú vị về một số câu hỏi trong câu trả lời cho câu hỏi này. Java generics - type erasure - when and what happens

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