2011-06-09 26 views
11

thể trùng lặp:
Why are not all type information erased in Java at runtime?truy cập thông tin kiểu tham số khi chạy

Generics Java được thực hiện thông qua loại tẩy xoá, vì vậy tôi nghĩ rằng nó là không thể nhận được bất kỳ thông tin về các loại tham số trong thời gian chạy. Tuy nhiên, tôi đã tìm thấy lớp sau trong thư viện Jackson.

(Tôi đã đơn giản hóa lớp hơi vì lợi ích của ví dụ này)

public abstract class TypeReference<T> { 
    final Type _type; 

    protected TypeReference() { 
     Type superClass = getClass().getGenericSuperclass(); 
     _type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; 
    } 

    public Type getType() { return _type; } 

} 

Lớp cung cấp quyền truy cập vào đó là kiểu tham số khi kiểm tra dưới đây trình bày:

void testTypeReference() { 
    // notice that we're instantiating an anonymous subclass of TypeReference 
    TypeReference<CurrencyDto> tr = new TypeReference<CurrencyDto>() {}; 
    assert tr.getType() == CurrencyDto.class 
} 

lớp này minh họa rằng các tham số kiểu thực tế có thể được lấy ra trong thời gian chạy (sử dụng sự phản chiếu), làm thế nào điều này phù hợp với khái niệm rằng Java Generics được thực hiện thông qua xóa bỏ kiểu?

+2

Tôi không cho rằng đây là bản sao của câu hỏi khác. Câu hỏi đó hỏi _why_ trình biên dịch lưu trữ thông tin đó, khi mà điều này yêu cầu một lời giải thích về cách điều này phù hợp với khái niệm xóa bỏ kiểu. – ColinD

Trả lời

0

Tôi thấy không có mâu thuẫn thực sự ở đây.

LoạiReference sẽ được coi là loại TypeReference thô khi chạy cho mã cũ, nhưng vẫn có khả năng cung cấp thông tin về SomeType. Loại xóa nghĩa là bạn không thể sử dụng T của TypeReference khi chạy là examplified here. Nhưng bạn vẫn có thể biết bằng những gì T được thay thế ..

4

Nó thực sự khá đơn giản: bạn không thể lấy thông tin chung từ INSTANCES giá trị, nhưng bạn có thể lấy nó từ TYPES (lớp), với một số hạn chế. Cụ thể, có 3 nơi có sẵn thông tin loại chung (xem http://www.cowtowncoder.com/blog/archives/2008/12/entry_126.html để biết chi tiết); trên khai báo siêu lớp/giao diện (tham số hóa siêu loại), trên khai báo trường, và và trên khai báo phương thức (đối số, kiểu trả về).

Trong trường hợp TypeReference, điều xảy ra là bạn tạo một loại ẩn danh với loại siêu quy định; và thông tin này sau đó sẽ có sẵn kể từ khi loại ẩn danh (lớp) được thông qua.

+0

"thực sự khá đơn giản" - Tôi sẽ không đồng ý với bạn ở đó. –

+0

Heh. Vâng, ý tưởng rằng VALUE không có thông tin loại chung, nhưng định nghĩa Lớp làm là đơn giản. Không phải generics như một toàn thể, rằng tôi sẽ không yêu cầu bồi thường. :) Nhưng FWIW, điều này (http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html) có thể làm cho nó có thể hiểu làm thế nào nó hoạt động. – StaxMan

+0

@StaxMan, cảm ơn bạn đã trả lời và liên kết thú vị. –

12

Bê tông thông tin đối số kiểu được lưu trữ trong tệp lớp khi được biết lúc biên dịch. Ví dụ: nếu bạn có một lớp học có phương thức trả về List<String> (không phải List<T>!) Thì thông tin đó sẽ có sẵn khi chạy. Tương tự như vậy, nếu bạn có một lớp:

public class Foo extends Bar<String> { 
} 

kiểu lập luận String là hardcoded để Foo tại thời gian biên dịch. Lớp TypeReference (và tất cả các cấu trúc tương tự, chẳng hạn như TypeLiteral trong Guice và TypeToken trong Gson) sử dụng thực tế này bằng cách yêu cầu bạn tạo lớp con ẩn danh của nó trong mã của bạn. Khi bạn làm điều này, một tệp lớp thực tế sẽ được tạo với thông tin đó, giống như ví dụ Foo ở trên.

Để biết thêm thông tin, hãy xem bài đăng trên blog của Neal Gafter here.

Loại xóa nhiều hơn đề cập đến thực tế là bạn không thể nhận thông tin về các đối số kiểu thực tế cho một thể hiện của kiểu chung khi chạy (lưu ý rằng Foo, một phân lớp của Bar<String>, không có biến kiểu nào). Ví dụ, nếu bạn tạo một thể hiện của kiểu generic ArrayList<E>:

thông tin
List<String> foo = new ArrayList<String>(); 

không có loại được lưu trữ trong đối tượng đó. Vì vậy, khi bạn chuyển cho một số phương pháp khác:

public <T> T foo(List<T> list) 

không có cách nào để tìm hiểu xem T là gì.

+0

Sẽ không chính xác hơn khi nói "không có cách nào để tìm ra T là gì, nếu danh sách trống?" Tôi cảm thấy một chút dày nhưng tôi không thể thực sự hòa giải nó trong đầu của tôi rằng thông tin loại có sẵn tại thời gian chạy nếu danh sách không rỗng bằng cách thực hiện foo.get (0) .getClass(). Mặc dù tôi nhận ra rằng chúng ta không nhất thiết phải lấy T vì các đối tượng trong danh sách có thể là các lớp con của T. –

+0

@Marcus Junius Brutus: 'T' không nhất thiết phải là loại phần tử đầu tiên. Ví dụ, phần tử đầu tiên có thể là 'Double' trong khi' T' là 'Số' hoặc' Đối tượng'. Ngoài ra, hãy nhớ rằng không phải mọi lớp chung chung là một 'List' hoặc một số container khác, nơi bạn có thể nhận được một thể hiện của' T' bằng cách gọi một phương thức. 'Comparator', ví dụ, chỉ có một phương thức mà bạn vượt qua các thể hiện của' T'. – ColinD

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