2010-10-15 34 views
9

這是我的代碼: ExecutorImp擴展AbstractExecutor,它提取執行程序的相同執行邏輯(ExecutorImp是一種情況),當調用ExecutorImp的execute()方法時,它將調用超類型的方法,但超類型(該AbstractExcutor)應該知道另一個類結合實施者(在本例中,它是User類):如何在運行時獲得泛型類型?

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; 
    } 
} 

那麼,什麼是我的代碼的問題?

+0

也許這個答案可以幫助您解決問題。 HTTP://計算器。com/questions/6624113/get-type-name-for-generic-parameter-of-generic-class/19454610#19454610 – 2017-01-12 01:01:23

回答

12

這裏有一些困惑。由於類型擦除你不能從運行時參數化類型一樣類型的信息:

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

但是,您可以通過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)); 
    } 

    // ... 
} 

更新:這是否推薦與否,雖然這會的工作,這是敏感的,只要在類聲明的微小變化發生運行時問題。你作爲開發者應該正確記錄它。作爲一個完全不同的選擇,你可以使用多態。

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); 

    // ... 
} 

並相應地實施UserExecutor

class UserExecutor extends AbstractExecutor<User> { 

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

    // ... 
} 
+0

非常感謝,並感謝本頁面中的所有人。 BalusC的答案是我想要的。但是,我想知道我的代碼是否被推薦?這是我的applaction上下文:http://forums.oracle.com/forums/message.jspa?messageID = 7006003#7006​​003 – hguser 2010-10-15 12:16:52

6

我認爲你應該使用getActualTypeParameters;因爲getTypeParameters並不是指您在當前實例中已放入的內容而不是E,而是指向E本身(以描述它是如何限定的等)。

爲了得到ParameterizedType,您應該首先使用getGenericSuperclass

更新:但以上只有當前對象從通用類派生與實例,就像一般的參數的工作原理:

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

應該返回String.class

+4

準確地說:getGenericSuperclass()只會在你的類不是泛型時給予有意義的結果類,所以:new LinkedList ().getClass()。getGenericSuperclass()對你來說是不夠的,但是:new LinkedList (){}。getClass()。getGenericSuperclass()就可以了 - 注意到區別?通過添加我創建的LinkedList的子類。 – iirekm 2010-10-15 11:23:53

+0

@iirekm true,那麼我想沒有辦法讓對象檢測它是如何反射聲明的,但它可以從「外部」完成(例如,使用Field.getGenericType())... – fortran 2010-10-15 13:03:00

2

我不認爲你可以在運行時獲得泛型類型。泛型類型是一個適用於編譯時的限制。正如我在運行時記住的那樣,泛型集合和沒有泛型類型的集合之間沒有區別。

+0

不完全真:通過'fortran'閱讀解決方案和我的評論。 – iirekm 2010-10-15 11:24:52

+2

他他,我曾經想過......擦除意味着在編譯時你可以忽略通用限制,因爲運行這兩個實例的字節碼是相同的,但是關於如何聲明實例的信息將保留。 – fortran 2010-10-15 12:56:59

1

解決問題的常用方法是稍微更改代碼。在接受Class<E>參數的基類上定義構造函數。將此參數分配給內部字段。

在子類上定義不帶參數的構造函數,並從那裏調用super(User.class)

這樣你就可以知道類的參數,而不會對子類的客戶負擔過重。