2012-06-22 59 views
8

Hãy tưởng tượng kịch bản sau đây:Phản ánh cho Lớp tham số chung trong Java?

class MyClass extends OtherClass<String>{ 

    String myName; 
    //Whatever 

} 

class OtherClass<T> { 

    T myfield; 

} 

Và tôi phân tích MyClass sử dụng phản chiếu đặc biệt (MyClass.class).getDeclaredFields(), trong trường hợp này tôi sẽ nhận được các lĩnh vực sau (và các loại, sử dụng getType() của Field):

myName --> String 
myField --> T 

Tôi muốn nhận được Loại thực tế cho T, được biết đến lúc chạy do "Chuỗi" rõ ràng trong ký hiệu mở rộng, làm cách nào để chuyển loại myField không di truyền?

CHỈNH SỬA GIẢI QUYẾT:

Có vẻ như câu trả lời là "bạn không thể". Đối với những người có thể xem xét câu hỏi này sau, tôi khuyên bạn nên sử dụng Jackson (tôi đã cố gắng làm điều này để tạo JSON) và chú thích các lớp và các trường của bạn theo cách sao cho Jackson biết về hệ thống phân cấp thừa kế và có thể tự động làm gì câu trả lời chính xác bên dưới được đề xuất.

+1

Câu trả lời không phải là bạn không thể; bạn có thể dễ dàng tạo một bản đồ từ các tham số kiểu tới các đối số kiểu rõ ràng. – Jeffrey

Trả lời

18

Điều này có thể đạt được chỉ với sự phản ánh bởi vì bạn đã sử dụng rõ ràng String, nếu không thông tin này sẽ bị mất do loại xóa.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String> 
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String> 
+0

Có vẻ như bạn thắng cuộc thi vì câu trả lời hoàn chỉnh :) – Jochen

+0

Đây là một câu trả lời tuyệt vời, tuy nhiên làm thế nào tôi có thể làm điều này nói chung với một đối tượng Field? Tôi không muốn yêu cầu một cách rõ ràng lớp siêu vì tôi chỉ cố gắng để có được tất cả các trường được khai báo của lớp con của tôi, một trong số đó xảy ra là chung chung. Tôi chỉ muốn một cái gì đó tương tự như getDeclaredFields(), nhưng thông minh hơn về Generics. Điều đó có ý nghĩa? –

+0

@hatboysam Bằng cách này hay cách khác, bạn phải yêu cầu lớp học siêu hạng. Việc gọi 'Field.getGenericParameter' sẽ chỉ trả về tham số kiểu (trong trường hợp này là' T'), không phải là đối số kiểu tường minh ('String'). Bạn có thể lấy tham số kiểu này và tìm đối số kiểu rõ ràng bằng cách tạo một bản đồ từ 'MyClass.class.getSuperclass(). GetTypeArguments()' đến 'MyClass.class.getGenericSuperclass(). GetActualTypeArguments()'. – Jeffrey

-4

Không có cách trực tiếp nào để nhận loại thực tế vì số Type Erasure. Tuy nhiên bạn có thể sử dụng các cách sau:

trong OtherClass<T> của bạn, hãy viết các phương pháp trừu tượng sau:

protected abstract class<T> getClazz(); 

Sau đó, trong MyClass, bạn thực hiện phương pháp này:

@Override 
protected Class<String> getClazz(){ 
    return String.class; 
} 

sau đó bạn có thể gọi getClazz() để có được lớp học.

+0

+1 cho liên kết để loại Xóa số – BlackVegetable

+2

OP đưa ra một tham số kiểu cụ thể cho lớp chung của mình, thông tin * được * giữ trong thời gian chạy và có thể được truy lục. – Jeffrey

+0

-1 - Giải pháp này là không cần thiết như những người khác đã lưu ý trên bài đăng này.Ngoài ra, 'getClass()' của bạn sẽ có một tên xung đột với phương thức cuối cùng ['Object # getClass()'] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object .html # getClass \ (\)) –

0

Loại chung là không phải là được biết khi chạy. Chỉ trình biên dịch biết về chúng, kiểm tra xem chương trình của bạn có được gõ đúng hay không và sau đó loại bỏ chúng.

Trong trường hợp cụ thể của bạn, gọi MyClass.class.getGenericSuperclass() có thể cung cấp cho bạn thông tin bạn cần, vì một số lý do lạ, các loại cụ thể được sử dụng khi kế thừa được giữ trong bộ mô tả lớp.

+2

Nó không phải là "một số lý do lạ". * Các khai báo * của các trường, phương thức, siêu lớp, các lớp kèm theo, vv luôn được giữ lại, bởi vì các lớp khác cần sử dụng chúng khi biên dịch. Đây là một vấn đề riêng biệt từ các kiểu đối tượng thời gian chạy, nơi không có generics. – newacct

3

Tôi tìm thấy một lời giải thích đẹp here:

Khi thời gian chạy kiểm tra một loại parameterizable chính nó, như java.util.List, không có cách nào để biết những gì loại được đã được tham số hóa để. Điều này có ý nghĩa vì loại có thể được tham số hóa cho tất cả các loại loại trong cùng một ứng dụng. Tuy nhiên, khi bạn kiểm tra phương thức hoặc trường khai báo việc sử dụng một kiểu tham số, bạn có thể thấy trong thời gian chạy kiểu kiểu paramerizable được tham số hóa.

Nói tóm lại:

Bạn không thể nhìn thấy trên một loại bản thân loại nó là tham số để một thời gian chạy, nhưng bạn có thể nhìn thấy nó trong các lĩnh vực và phương pháp, nơi nó được sử dụng và tham số.

Trong mã:

Bạn không thể nhìn thấy T đây:

class MyClass<T> { T myField; } 

Bạn có thể xem "T" ở đây:

class FooClass { 
    MyClass<? extends Serializable> fooField; 
} 

Ở đây bạn sẽ có thể cho biết các nhập và nhập các thông số của fooField. Xem các phương thức getGeneric*() của ClassMethod.

Bằng cách này, tôi thường thấy điều này (rút ngắn):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0]; 

này là không đúng, bởi vì getActualTypeArguments() có thể, và thường sẽ, trở TypeVariable thay vì lớp - đó là khi chung là <? extends SomeClass> thay vì chỉ <SomeClass>. Nó có thể đi sâu hơn, hãy tưởng tượng:

class FooClass { 
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField; 
} 

Vì vậy, bạn sẽ có được một cây Type s. Nhưng đó là một chút chủ đề. Thưởng thức :)

0

Đây là một ví dụ điển hình về lý do tại sao phản chiếu không phải là một ý tưởng tuyệt vời.

Những gì bạn có thể nhận được từ một chương trình bằng cách phản ánh chỉ là những sự kiện mà người biên dịch cho ngôn ngữ đã chọn để cung cấp.

Và thông thường, họ không thể đủ khả năng để cung cấp mọi thứ có sẵn; họ sẽ phải giữ nguyên văn bản chương trình thô.

Tất cả các sự kiện khác về mã của bạn do đó không có sẵn cho người phản ánh.

Cách khắc phục này là bước bên ngoài ngôn ngữ và sử dụng công cụ có thể cung cấp bất kỳ thông tin tùy ý nào về mã. Các công cụ này được gọi là Program Transformation Systems (PTS).

PTS phân tích cú pháp mã nguồn và xây dựng một AST đại diện cho nó. Một PTW tốt sẽ xây dựng một AST chứa tất cả mọi thứ về mã (toán tử, toán hạng, dấu chấm câu, bình luận) để nó có thể được kiểm tra. Thông thường một PTS sẽ ghi lại vị trí dòng/cột của mã thông báo ngôn ngữ, do đó, ngay cả thông tin bố cục có sẵn; PTS cực đoan sẽ ghi lại khoảng trắng trong khoảng trống giữa các mã thông báo hoặc ít nhất là biết cách đọc tệp văn bản gốc khi cần nếu được hỏi về nó. AST này là bản chất tương đương với toàn bộ văn bản tôi nói sẽ là cần thiết, nhưng trong một hình thức thuận tiện hơn để xử lý.

(PTSs có một thuộc tính rất đẹp khác: chúng có thể sửa đổi AST và tạo lại mã cho chương trình đã sửa đổi. Nhưng đó là ở trên và vượt quá sự phản chiếu để tôi không bình luận thêm về khía cạnh này).

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