1

我想使用通配符作为类型在类中设置一个列表:

 public List<?> setList(List<?> list){
      this.list = list;
 }

但我只接受某些类型的 List 元素。该示例仅包含 String 方法,但我将接受 String、Double 或 Integer fe。

我知道这种方法:

 public void setList(List<?> list){
     if(!list.isEmpty())
         if(list.get(0).getClass().equals(String.class))
              this.list = list;
         else
              throw new IllegalArgumentException();
     else 
         // what to do?
 }

如果列表不为空,我可以检查第一个元素类。如果它是 String.class 我将设置列表,否则将抛出异常。

但是如果列表为空,我应该如何反应?因为 AFAIK 由于类型擦除,我无法检查通配符的类型。所以,我不能实例化一个空列表。

我应该这样做this.list = null;吗?还是我应该只接受非空列表并在它为空时抛出异常?我很感激任何建议。因为这段代码将被重用,并且应该告知使用它的人会发生什么。

4

2 回答 2

2

直接回答您的问题:空列表没有问题。

由于类型擦除,在运行时 List<String>、List<Double> 或简单 List 之间没有区别。

您甚至可以欺骗编译器并执行以下操作

List<String> stringList = new ArrayList<String>();
stringList.add("foo");
List rawList = stringList;
rawList.add(42);
List<Integer> integerList = rawList;

对同一列表的三个引用仅由编译器进行不同处理。(你会得到一些警告取决于你的编译器设置,但仅此而已。)

如果要在运行时检查一致性,则必须检查列表的每个成员

public <T> void setList(List<T> list, Class<T> clazz) {
    for (T t : list) {
        if(!clazz.equals(t.getClass())) {
            throw new YourPreferedKindOfException();
        }
    }
    this.list = list;
}

(检查省略的空参数)

合适的异常可能是 IllegalArgumentException,称为 eg

setList(stringList, String.class);

为了在运行时真正确定,您必须复制列表,因为您的函数将列表作为参考,并且稍后可以在您的函数之外修改它的内容。

于 2013-09-07T16:06:47.133 回答
1

我在这里有三种方法:

  • 简单的通用方法

您必须对保存列表的类进行参数化。通过创建具有指定泛型类型的新实例。那将是一种类型安全的方法。

public class ListHolder<T> {

    private List<T> list;

    public void setList(List<T> list) {
        this.list = list;
    }
    public static void main(String[] args) {
        ListHolder<String> sl = new ListHolder<String>();
        sl.setList(new ArrayList<String>());

        ListHolder<Double> dl = new ListHolder<Double>();
        dl.setList(new ArrayList<Double>());
    }
}
  • 基于动态许可的方法

在这里,我们用允许的类填充一个集合。

public class ListHolder {
    private Set<Class<?>> allowed = new HashSet<Class<?>>();
    private List<?> list;

    public <T> void setList(List<T> list) {
        if (list.isEmpty()) {
            // problem is we can't check type here. Exception would be better
            list = Collections.<T> emptyList();
        } else {
            Class<?> clazz = list.get(0).getClass();
            if (allowed.contains(clazz)) {
                this.list = list;
            } else {
                throw new IllegalArgumentException("Type "
                        + clazz.getSimpleName() + " not allowed!");
            }
        }
    }

    public void allowClass(Class<?> clazz) {
        allowed.add(clazz);
    }
}
  • 基于静态许可的方法

这里的检查是在编译时进行的。

public class ListHolder {

    private List<?> list;

    public static final Allowed<String> STRING = new Allowed<String>();
    public static final Allowed<Double> DOUBLE = new Allowed<Double>();

    public <T> void setList(List<T> list, Allowed<T> permission) {
        if (permission == null)
            throw new NullPointerException("Permission is null!");
        this.list = list;
        if (list == null) // if you don't want list to be null
            this.list = Collections.<T>emptyList(); // added type parameter to make it clearer for reader
        // ...compiler does that implicitly
    }

    private static class Allowed<T> {
    }

    public static void main(String[] args) {
        ListHolder l = new ListHolder();

        List<String> strings = new ArrayList<String>();
        List<Double> doubles = new ArrayList<Double>();
        List<Integer> integers = new ArrayList<Integer>();

        l.setList(strings, ListHolder.STRING);
        l.setList(doubles, ListHolder.DOUBLE);
        //l.setList(integers, ListHolder.DOUBLE); Does not even compile
    }
}
于 2013-09-07T12:02:33.003 回答