2010-10-15 25 views
9

Đây là mã của tôi: ExecutorImp mở rộng AbstractExecutor trích xuất cùng một logic thực thi của các trình thực thi của nó (ExecutorImp là một trường hợp), khi gọi phương thức execute() của ExecutorImp, nó sẽ gọi phương thức trong siêu kiểu của nó, nhưng siêu kiểu (AbstractExcutor) nên biết một lớp khác ràng buộc với người triển khai (trong ví dụ, đó là lớp Người dùng):Làm thế nào để có được kiểu generic trong thời gian chạy?

import java.lang.reflect.InvocationTargetException; 
import java.util.ArrayList; 

abstract class AbstractExecutor<E> { 
    public void execute() throws Exception { 
     ArrayList<E> list = new ArrayList<E>(); 
     // here I want to get the real type of 'E' 
     Class cl = this.getClass().getTypeParameters()[0].getGenericDeclaration().getClass(); 
     Object o = cl.getConstructor(String.class).newInstance("Gate"); 
     list.add((E) o); 
     System.out.println(format(list)); 
    } 
    public abstract String format(ArrayList<E> list); 
    public abstract String getType(); 
} 

public class ExectorImp<E> extends AbstractExecutor<User> { 
    @Override 
    public String getType() { 
     return "user"; 
    } 
    @Override 
    public String format(ArrayList<User> list) { 
     StringBuffer sb = new StringBuffer(); 
     for (User u : list) { 
      sb.append(u.toString() + " "); 
     } 
     return sb.toString(); 
    } 
    public static void main(String[] args) throws Exception { 
     new ExectorImp().execute(); 
    } 
} 
class User { 
    String name; 
    public User(String name) { 
     this.name = name; 
    } 
} 

SO, vấn đề với mã của tôi là gì?

+0

Có thể câu trả lời này có thể giúp giải quyết vấn đề của bạn. http: // stackoverflow.com/questions/6624113/get-type-name-for-generic-parameter-of-generic-class/19454610 # 19454610 –

Trả lời

12

Có một số nhầm lẫn ở đây. Do loại tẩy xoá bạn không thể gõ thông tin từ runtime kiểu tham số như:

Class<E> cls = E.getClass(); // Error. 
E e = new E(); // Error. 

Tuy nhiên, bạn có thể lấy thông tin compiletime kiểu tham số từ lớp, lĩnh vực và phương pháp kê khai bởi ParameterizedType#getActualTypeArguments().

abstract class AbstractExecutor<E> { 

    public void execute() throws Exception { 
     List<E> list = new ArrayList<E>(); 
     Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
     E e = cls.getConstructor(String.class).newInstance("Gate"); 
     list.add(e); 
     System.out.println(format(list)); 
    } 

    // ... 
} 

Cập nhật: là liệu này được khuyến khích hay không, mặc dù điều này sẽ làm việc, đây là nhạy cảm với thời gian chạy khó khăn khi thay đổi nhỏ trong khai báo lớp xảy ra. Bạn là nhà phát triển nên ghi lại tài liệu đúng cách. Là một phương án hoàn toàn khác, bạn có thể sử dụng đa hình.

abstract class AbstractExecutor<E> { 

    public void execute() throws Exception { 
     List<E> list = new ArrayList<E>(); 
     E e = create("Gate"); 
     list.add(e); 
     System.out.println(format(list)); 
    } 

    public abstract E create(String name); 

    // ... 
} 

và triển khai UserExecutor tương ứng.

class UserExecutor extends AbstractExecutor<User> { 

    @Override 
    public User create(String name) { 
     return new User(name); 
    } 

    // ... 
} 
+0

Cảm ơn bạn rất nhiều và cảm ơn tất cả các bạn trong trang này. Câu trả lời của BalusC là những gì tôi muốn.Tuy nhiên, tôi tự hỏi liệu mã của tôi có được khuyến nghị không? Đây là ngữ cảnh applaction của tôi: http: //forums.oracle.com/forums/message.jspa? MessageID = 7006003 # 7006003 – hguser

6

Tôi nghĩ bạn nên sử dụng getActualTypeParameters; dưới dạng getTypeParameters không đề cập đến những gì đã được đưa vào bản trình bày hiện tại của bạn thay cho số E, nhưng với chính nó là E (để mô tả cách nó được bao bọc, v.v.).

Để nhận được số ParameterizedType, bạn nên sử dụng getGenericSuperclass trước tiên.

cập nhật: nhưng ở trên chỉ có tác dụng nếu đối tượng hiện nay có nguồn gốc từ một lớp học chung với lập luận chung instantiated, như:

class StringList extends ArrayList<String> { 
    public Type whatsMyGenericType() { 
     return ((ParameterizedType)getGenericSuperClass()).getActualTypeParameters()[0]; 
    } 
} 

nên trở String.class.

+4

Chỉ cần chính xác: getGenericSuperclass() sẽ cho kết quả có ý nghĩa cho bạn chỉ khi lớp không phải là một generic class, vì vậy: new LinkedList () .getClass() getGenericSuperclass() sẽ không đủ cho bạn, nhưng: new LinkedList () {}. getClass(). getGenericSuperclass() là OK - nhận thấy sự khác biệt? Bằng cách thêm {} tôi tạo lớp con của LinkedList. – iirekm

+0

@iirekm true, sau đó tôi đoán không có cách nào để một đối tượng phát hiện nó được khai báo một cách phản ánh, nhưng nó có thể được thực hiện "từ bên ngoài" (sử dụng 'Field.getGenericType()', chẳng hạn) ... – fortran

2

Tôi không nghĩ rằng bạn có thể nhận được loại chung khi chạy. Loại chung là một hạn chế áp dụng tại thời gian biên dịch. Như tôi nhớ trong thời gian chạy, không có sự khác biệt giữa bộ sưu tập chung và bộ sưu tập không có loại chung.

+0

Không hoàn toàn true: đọc giải pháp của 'fortran' và bình luận của tôi cho nó. – iirekm

+2

Ông ấy, tôi nghĩ rằng một lần nữa ... Việc xóa nghĩa là lúc biên dịch bạn có thể bỏ qua những hạn chế chung như bytecode chạy cả hai trường hợp là như nhau, nhưng thông tin về cách các cá thể được khai báo được giữ lại. – fortran

1

Cách tiếp cận thông thường để khắc phục sự cố là hơi thay đổi mã. Xác định hàm tạo trên lớp cơ sở chấp nhận tham số Class<E>. Gán tham số này cho trường nội bộ.

Trên phân lớp xác định hàm tạo mà không có tham số và gọi super(User.class) từ đó.

Bằng cách này, bạn sẽ biết lớp đối số mà không bị quá tải nhiều cho khách hàng của lớp con.

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