我可以让这个javapulk()方法更安全吗



我编写了这个实用程序函数:

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()返回的数组是否包含所需的类。除此之外,你运气不好。

相关内容

  • 没有找到相关文章

最新更新