如何将Class作为参数传递并在Java中返回泛型集合?

Jon*_*nas 37 java generics polymorphism class parameter-passing

我正在为我的Java应用程序设计一个简单的数据访问对象.我有一些类(记录)代表表中的单行UserFruit.

我想有一个方法来获取特定类型的所有记录.

目前我有这样的:

public List<User> getAllUsers() {
 ...
}

public List<Fruit> getAllFruits() {
 ...
}

....
Run Code Online (Sandbox Code Playgroud)

但我想有一个像这样的单一多态方法(错误):

public List<T> getAllRecords(Class<T> type) {
    if(type instanceof User) {
        // Use JDBC and SQL SELECT * FROM user
    } else if(type instanceof Fruit) {
        // Use JDBC and SQL SELECT * FROM fruit
    }
    return collection;
}
Run Code Online (Sandbox Code Playgroud)

用途示例:

List<Fruit> fruits = myDataAccessObject.getAllRecrods(Fruit.class);
List<User> users = myDataAccessObject.getAllRecords(User.class);
Run Code Online (Sandbox Code Playgroud)

我怎么能用Java做到这一点?

pol*_*nts 27

看起来你想要适应Josh Bloch所谓的Typesafe Heterogenous Container模式:你传递的是一个类型令牌Class<T>,你想要回来一个List<T>.

普通的旧THC可以以类型安全的方式Class<T>将a 映射到a T,但由于你实际上想要一个List<T>,所以你想要使用Neal Gafter所谓的超类型令牌.

以下片段改编自Neal Gafter博客中发布的Crazy Bob Lee的代码:

public abstract class TypeReference<T> {
    private final Type type;

    protected TypeReference() {
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof Class<?>) {
            throw new RuntimeException("Missing type parameter.");
        }
        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }
    public Type getType() {
        return this.type;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以创建一个超类型令牌,如下所示:

    TypeReference<String> stringTypeRef =
            new TypeReference<String>(){};

    TypeReference<Integer> integerTypeRef =
            new TypeReference<Integer>(){};

    TypeReference<List<Boolean>> listBoolTypeRef =
            new TypeReference<List<Boolean>>(){};
Run Code Online (Sandbox Code Playgroud)

基本上你传递一个TypeReference<T>而不是一个Class<T>.不同的是,没有List<String>.class,但你可以做一个TypeReference<List<String>>.

所以现在我们可以按如下方式制作我们的容器(以下内容改编自Josh Bloch的原始代码):

public class Favorites {
    private Map<Type, Object> favorites =
        new HashMap<Type, Object>();

    public <T> void setFavorite(TypeReference<T> ref, T thing) {
        favorites.put(ref.getType(), thing);
    }
    public <T> T getFavorite(TypeReference<T> ref) {
        @SuppressWarnings("unchecked")
        T ret = (T) favorites.get(ref.getType());
        return ret;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以将两者放在一起:

    Favorites f = new Favorites();
    f.setFavorite(stringTypeRef, "Java");
    f.setFavorite(integerTypeRef, 42);
    f.setFavorite(listBoolTypeRef, Arrays.asList(true, true));

    String s = f.getFavorite(stringTypeRef);
    int i = f.getFavorite(integerTypeRef);
    List<Boolean> list = f.getFavorite(listBoolTypeRef);

    System.out.println(s);    // "Java"
    System.out.println(i);    // "42"
    System.out.println(list); // "[true, true]"
Run Code Online (Sandbox Code Playgroud)

Neal Gafter在他的博客中指出,TypeReference对于超级类型的令牌而言,更多类型的令牌将在JDK中得到有价值的包含.

附件

参考


Emi*_*mil 25

既然你说你不希望你在不同的类中使用数据访问方法(在对anish的答案的评论中),我想为什么不尝试这样的东西.

public class Records {

    public interface RecordFetcher<T>{
        public List<T> getRecords();
    }
    static RecordFetcher<Fruit> Fruit=new RecordFetcher<Fruit>(){
        public List<Fruit> getRecords() {
            ...
        }
    };


    static RecordFetcher<User> User=new RecordFetcher<User>(){
        public List<User> getRecords() {
            ...
        }   
    };

    public static void main(String[] args) {
        List<Fruit> fruitRecords=Records.Fruit.getRecords();
        List<User> userRecords=Records.User.getRecords();

    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:

我想再添加一个我的实现.

public class Test 
{ 
    public static void main(String[] args) 
    { 
       Test dataAccess=new Test();
       List<Fruit> FruitList=dataAccess.getAllRecords(Fruit.myType);
       List<User> UserList=dataAccess.getAllRecords(User.myType);
    } 
    <T> List<T> getAllRecords(T cl)
    {
        List<T> list=new ArrayList<T>();
        if(cl instanceof Fruit)
        {
             // Use JDBC and SQL SELECT * FROM fruit
        }
        else if(cl instanceof User)
        {
            // Use JDBC and SQL SELECT * FROM user
        }
        return list;
    }
}
class Fruit
{
    static final Fruit myType;
    static {myType=new Fruit();}
}
class User
{
    static final User myType;
    static {myType=new User();}
}
Run Code Online (Sandbox Code Playgroud)

编辑:

我认为这个实现就像你问的那样

public class Test 
{ 
    public static void main(String[] args) throws InstantiationException, IllegalAccessException 
    { 
       Test dataAccess=new Test();

       List<Fruit> FruitList=dataAccess.getAllRecords(Fruit.class);

       List<User> UserList=dataAccess.getAllRecords(User.class);

    } 
    <T> List<T> getAllRecords(Class<T> cl) throws InstantiationException, IllegalAccessException
    {
        T inst=cl.newInstance();
        List<T> list=new ArrayList<T>();
        if(inst instanceof Fruit)
        {
             // Use JDBC and SQL SELECT * FROM user
        }
        else if(inst instanceof User)
        {
            // Use JDBC and SQL SELECT * FROM fruit
        }
        return list;
    }
}
Run Code Online (Sandbox Code Playgroud)


jjn*_*guy 13

你很近.

public <T> LinkedList<T> getAllRecords(List<T> list) {
 ...
}
Run Code Online (Sandbox Code Playgroud)

这称为通用方法.

您需要指定一个参数List<T>.然后,根据您传入的列表的类型,Java将推断要返回的泛型类型.

编辑:

保利的答案非常好.您应该很容易执行以下操作,而不必创建TypeReference类.

List<Fruit> fruit = myDataAccessObject.getAllRecrods(new LinkedList<Fruit>());
List<User> users = myDataAccessObject.getAllRecords(new LinkedList<User>());
Run Code Online (Sandbox Code Playgroud)


f1s*_*1sh 5

根据您实际检索数据的方式,您可以执行以下操作:

private static <T> List<T> getAll(Class<T> cls){
  List<T> fromSql = (List<T>) sql.query("SELECT * FROM objects WHERE type="+cls.getName());
  return fromSql;
}
Run Code Online (Sandbox Code Playgroud)

这要求你的sql对象返回正确的列表类型,像 iBatis 这样的 O/R 映射器就是这样做的。

如果您需要区分传递的类型,您仍然可以在cls.