我编写了这个实用程序函数:
public static <T> List<T> pluck(String fieldName, List list)
throws NoSuchFieldException, IllegalAccessException {
if (list.isEmpty()) {
return new ArrayList<T>();
}
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<T> result = Lists.newArrayList();
for (Object object : list) {
result.add((T) f.get(object));
}
return result;
}
我从undercore.js中复制了这个想法。用例是:
ArrayList<Person> people = new ArrayList<Person>;
people.add(new Person("Alice", "Applebee"));
people.add(new Person("Bob", "Bedmington"));
people.add(new Person("Charlie", "Chang"));
List<String> firstNames = pluck("firstName", people);
我的问题是,如果调用者的类型错误,那么在调用者尝试从列表中获取对象之前,不会引发异常。理想情况下,我想从pluck
方法本身抛出一个ClassCastException
。然而,我看不到在运行时访问列表类型的方法。
有没有什么技巧可以确保来电者不会收到无效列表?
编辑:所以根据我得到的反馈,一个安全的实现是:
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
throws NoSuchFieldException, IllegalAccessException {
Field f = listType.getField(fieldName);
ArrayList<F> result = new ArrayList<F>();
for (T element : list) {
result.add(fieldType.cast(f.get(element)));
}
return result;
}
但实际上lambdaj似乎做了我想做的事,所以我想我会用它。谢谢迈克!
免责声明:LambdaJ(@GoogleCode|@GitHub)-自JDK8(JSR335,JEP126)发布以来,此项目不再维护。
您可以将签名更改为:
public static <T, F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
您有列表类型和字段类型。
为什么不这样定义签名:
public static <T, U> List<T> pluck(String fieldName, Class<T> fieldType, List<U> list);
这将:
1) 强制客户端提供他想要"提取"的字段的类型,这样您就可以在方法中进行正确的类型检查。
2) 强制客户端提供一个要从中"提取"的通用列表,这样可以防止另一个错误源(客户端提供的列表包含不同类型的对象)。
我认为这是最安全的。。
无效列表是什么意思?如果您的意思是,他们试图将其强制转换为非强制转换,那么请尝试将声明更改为public static <T> List<T> pluck(String fieldName, List<T> list)
。
我被However, I don't see a way to access the type of the list on run time.
的评论弄糊涂了。然而,如果我理解正确的话:运行时没有"类型",因为Java中的泛型是通过"擦除"实现的。这意味着编译器在编译时检查它是否工作,然后将它转换为常规的强制转换,就像我们在泛型之前一样。他们认为这是实现前后兼容性所必需的。
您应该为类型参数使用泛型,并传入返回类型的类对象:
public static <TItem, TResult> List<TResult> pluck(String fieldName, List<TItem> list, Class<TResult> resultType)
throws NoSuchFieldException, IllegalAccessException {
if(list.isEmpty()) return new ArrayList<TResult>();
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<TResult> result = new ArrayList<TResult>();
for(Object object : list) {
result.add(resultType.cast(f.get(object)));
}
return result;
}
通常,当您收到关于类型参数的不安全强制转换的警告时,您应该看看是否可以将其替换为对Class.cast
的调用
你可以尝试谷歌收藏库提供的一个,而不是维护一个新的:collections.transform像这个
Collection<Y> yourCollection...
...
Collection<X> expected = Collections2.transform(yourCollection, new Function<Y, X>() {
public X apply(Y element) {
return element.getX();
}
}
使用谷歌的Guava收藏库,您可以使用Collections2.transform()
。
用法
给定一个接口/类,例如Entity
,您的类可以实现/扩展它。
public abstract class Entity {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
public interface Entity {
long getId();
}
现在,您可以检索每个Entity
的ID的列表。
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Main {
public static void main(String[] args) {
List<Entity> entities = ...
List<Long> ids = pluckIds(entities);
}
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return new ArrayList<Long>(Collections2.transform(list, new Function<E, Long>() {
public Long apply(E entity) {
return entity.getId();
}
});
}
}
这是你能得到的最安全的。这满足了适当的OOP原则和Java 5-7。
在Java 8中,您可以使用流、映射和lambda 来实现相同的效果
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return list.stream().map(e -> e.getId()).collect(Collectors.toList());
}
或
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType) throws NoSuchFieldException,
IllegalAccessException, IllegalArgumentException {
Field f = listType.getDeclaredField(fieldName);
f.setAccessible(true);
return list.stream().map(e -> {
try { return fieldType.cast(f.get(e)); } catch (Exception e1) { return null; }
}).collect(Collectors.toList());
}
不确定你在问什么,但你可以试试:
Class c = list.get(0).getClass();
if (!c.equals(Person.class))
throw new ClassCastException();
您可以将列表强制转换为java.lang.reflect.ParameterizedType
,并检查getActualTypeArguments()
返回的数组是否包含所需的类。除此之外,你运气不好。