如何将类作为参数传递并在 Java 中返回泛型集合?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3397160/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I pass a Class as parameter and return a generic collection in Java?
提问by Jonas
I am designing a simple Data Access Object for my Java application. I have a few classes (records) that represents a single row in tables like User
and Fruit
.
我正在为我的 Java 应用程序设计一个简单的数据访问对象。我有几个类(记录)代表表中的单行,如User
和Fruit
。
I would like to have a single method for getting all records of a specific type.
我想要一种方法来获取特定类型的所有记录。
For the moment I have it like this:
目前我是这样的:
public List<User> getAllUsers() {
...
}
public List<Fruit> getAllFruits() {
...
}
....
But I would like to have a single polymorphic method like this (wrong):
但我想要一个这样的多态方法(错误):
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;
}
Example for uses:
使用示例:
List<Fruit> fruits = myDataAccessObject.getAllRecrods(Fruit.class);
List<User> users = myDataAccessObject.getAllRecords(User.class);
How can I do this in Java?
我怎样才能在 Java 中做到这一点?
采纳答案by Emil
Since you say that you don't want you data access methods in different classes(in the comment to anish's answer),I thought why not try something like this.
既然你说你不希望你在不同的类中使用数据访问方法(在 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();
}
}
EDIT:
编辑:
I would like to add one more of my implementation.
我想再添加一个我的实现。
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();}
}
EDIT:
编辑:
I think this implementation is just as you have asked
我认为这个实现就像你问的那样
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;
}
}
回答by jjnguy
You are pretty close.
你很接近。
public <T> LinkedList<T> getAllRecords(List<T> list) {
...
}
This is called a Generic Method.
这称为通用方法。
You will want to specify a parameter like List<T>
. Then, based upon the type of the list you pass in, Java will infer the generic type to return.
您将需要指定一个参数,如List<T>
. 然后,根据您传入的列表类型,Java 将推断要返回的泛型类型。
Edit:
编辑:
Poly's answer is very good. It should be easy enough for you to do the following and not have to create a TypeReference
class.
保利的回答很好。执行以下操作应该很容易,而不必创建TypeReference
类。
List<Fruit> fruit = myDataAccessObject.getAllRecrods(new LinkedList<Fruit>());
List<User> users = myDataAccessObject.getAllRecords(new LinkedList<User>());
回答by polygenelubricants
It looks like you want to adapt what Josh Bloch calls a Typesafe Heterogenous Containerpattern: you are passing a type token Class<T>
, and you want back a List<T>
.
看起来你想要适应 Josh Bloch 所说的类型安全异构容器模式:你传递一个类型令牌Class<T>
,你想要返回一个List<T>
.
Plain old THC can map a Class<T>
to a T
in a typesafe manner, but since you actually want a List<T>
instead, then you want to use what Neal Gafter calls the super type tokens.
普通的老式 THC 可以以类型安全的方式Class<T>
将 a映射到 a T
,但由于您实际上想要 aList<T>
代替,那么您想使用 Neal Gafter 所说的超级类型 tokens。
The following snippet is adapted from Crazy Bob Lee's code posted in Neal Gafter's blog:
以下代码段改编自 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;
}
}
Now you can create a super type tokenlike these:
现在你可以创建一个像这样的超级类型令牌:
TypeReference<String> stringTypeRef =
new TypeReference<String>(){};
TypeReference<Integer> integerTypeRef =
new TypeReference<Integer>(){};
TypeReference<List<Boolean>> listBoolTypeRef =
new TypeReference<List<Boolean>>(){};
Essentially you pass a TypeReference<T>
instead of a Class<T>
. The difference is that there is no List<String>.class
, but you can make a TypeReference<List<String>>
.
本质上,您通过 aTypeReference<T>
而不是 a Class<T>
。不同之处在于没有List<String>.class
,但您可以制作一个TypeReference<List<String>>
.
So now we can make our container as follows (the following is adapted from Josh Bloch's original code):
所以现在我们可以按如下方式制作我们的容器(以下内容改编自 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;
}
}
Now we can put the two together:
现在我们可以将两者放在一起:
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]"
Neal Gafter argued in his blog that with some more bells and whistles, TypeReference
for super type tokens will make a worthy inclusion in the JDK.
Neal Gafter 在他的博客中认为,如果有更多的花里胡哨,TypeReference
超级类型令牌将值得包含在 JDK 中。
Attachments
附件
References
参考
回答by aNish
Well, I really don't know if you need it this way. But here is a polymorphic approach. It might help somewhere somehow.
嗯,我真的不知道你是否需要这样。但这是一种多态方法。它可能以某种方式在某处有所帮助。
Create different objects for different tables all implementing a common interface. This means you represent each table as an object.
为不同的表创建不同的对象,这些对象都实现了一个公共接口。这意味着您将每个表表示为一个对象。
import java.util.LinkedList;
public class DataAccessTest
{
/**
* @param args
*/
public static void main(String[] args)
{
DataAccess myDataAccessObject = new DataAccess();
Type type1 = new Fruit();
Type type2 = new User();
LinkedList<Type> list1 = myDataAccessObject.getAllRecords(type1);
LinkedList<Type> list2 = myDataAccessObject.getAllRecords(type2);
LinkedList<Type> list3 = myDataAccessObject.getAllRecords(new Fruit());
LinkedList<Type> list4 = myDataAccessObject.getAllRecords(new User());
}
}
class DataAccess
{
public LinkedList<Type> getAllRecords(Type type)
{
return type.getAllRecords();
}
}
interface Type
{
public LinkedList<Type> getAllRecords();
}
class Fruit implements Type
{
public LinkedList<Type> getAllRecords()
{
LinkedList<Type> list = new LinkedList<Type>();
list.add(new Fruit());
return list;
}
}
class User implements Type
{
public LinkedList<Type> getAllRecords()
{
LinkedList<Type> list = new LinkedList<Type>();
list.add(new User());
return list;
}
}
回答by f1sh
Depending on how you actually retrieve your data, you can do something like this:
根据您实际检索数据的方式,您可以执行以下操作:
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;
}
This requires your sql
object to return the correct type of list, which O/R mappers like iBatis do.
这要求你的sql
对象返回正确的列表类型,像 iBatis 这样的 O/R 映射器就是这样做的。
If you need to differentiate between the passed types, you can still do a switch/case on cls
.
如果您需要区分传递的类型,您仍然可以在cls
.
回答by Emily L.
I believe what you are trying to do is possible with a bit of generics magic. I had to solve the same problem just now and this is what I did:
我相信您尝试做的事情可以通过一些泛型魔法来实现。我刚才必须解决同样的问题,这就是我所做的:
public class ListArrayUtils{
@SuppressWarnings("unchecked") // It is checked.
public static <T,E> List<T> filterByType(List<E> aList, Class<T> aClass){
List<T> ans = new ArrayList<>();
for(E e: aList){
if(aClass.isAssignableFrom(e.getClass())){
ans.add((T)e);
}
}
return ans;
}
}
And unit tests:
和单元测试:
public class ListArrayUtilsTest{
interface IfA{/*nothing*/}
interface IfB{/*nothing*/}
class A implements IfA{/*nothing*/}
class B implements IfB{/*nothing*/}
class C extends A implements IfB{/*nothing*/}
@Test
public void testFilterByType(){
List<Object> data = new ArrayList<>();
A a = new A();
B b = new B();
C c = new C();
data.add(a);
data.add(b);
data.add(c);
List<IfB> ans = ListArrayUtils.filterByType(data, IfB.class);
assertEquals(2, ans.size());
assertSame(b, ans.get(0));
assertSame(c, ans.get(1));
}
}