Java泛型通配符 ? 快速手册
前言
故事的开始在于完成 MIT6.830 lab3 exercise3 的时候遇到了如下的代码:
Class<?> c = Class.forName("simpledb.execution.HashEquiJoin");
开始不明所以起来了,发现触及了自己的知识盲区,了解之后发现还十分有用,刚好可以让 Exercise 2 的屎山代码得到优化,故记录
Exercise 2 的情景如下:
现在有两个类 :IntHistogram 类 和 StringHistogram 类, 留下本讲需要的关键结构后长这样:
// Class IntHistogram
public class IntHistogram {
public double estimateSelectivity(Predicate.Op op, int v){...}
...
}
// Class StringHistogram
public class StringHistogram {
public double estimateSelectivity(Predicate.Op op, String s){...}
...
}
现在有一个 Field 类型的数组 :ArrarList<Field> arr , 而 Field 有两个子类 IntField 和 StringField
你需要做的是:
根据 Field 子类的类型选择不同的 Histogram , 然后将它们存在一个容器中,之后会不断的调用 estimateSelectivity 方法.
但是由于 IntHistogram 类 和 StringHistogram 类都没有继承父类并且方法的参数也不一样, 所以最开始新建了一个父类搞来搞去,最后成了屎山…..
? 无界通配符
最直接的理解就是 ? 可以用来表示所有的类型,我们之前在定义 ArrarList 的时候, 需要在 <> 中指定这个数组中要存入的类型,就比如 ArrarList<String> arr
但是有些时候我们不知道要存入的类型,或者说我们想在一个数组中存储不同的类
当然你可以直接 ArrarList<Object> arr , 但是还有另外一种更加优雅的方式 ArrarList<?> arr
但是这样操作也是有代价的,最主要的就是 不能在这个数组中插入元素了
// Error
public static void main(String[] args) {
ArrayList<?> arr = new ArrayList<>();
arr.add("error"); arr.add(-1)
}
所以无界通配符一般用于作为传入的函数中
public static void printf(ArrayList<?> arr) {
for(Object o : arr)
System.out.print(o + " ");
System.out.println();
}
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<>(
List.of(new String[]{"aaa", "bbb", "ccc"}));
ArrayList<Integer> arr2 = new ArrayList<>(
List.of(new Integer[]{1, 2, 3}));
printf(arr); printf(arr2);
}
/*
aaa bbb ccc
1 2 3
*/
基本的用法就这…主要是灵活运用
<? extends E> 上界通配符
在无界通配符中,ArrayList<?> arr 会存入所有继承自 Object 类的子类
但是如果我们在后面使用了 extends E , 那么就只能存入 E 以及 E 的子类了
不过还是要注意 : 不能在这个数组中插入元素了
<? super E> 上界通配符
与上界通配符类似, 但是这次是只能存入 E 以及 E 的父类了
注意, 这个时候是可以插入数据的
当我们将 E 变成 Object 的时候就可以完成许多神奇操作
回到我们上面提到的问题,在学完通配符后有以下两种方式:
法一:
public static ArrayList<? super Object> arr = new ArrayList<>();
public double estimateSelectivity(Predicate.Op op, Object v, int i) {
if(arr.get(i).getClass().equals(IntHistogram.class))
return ((IntHistogram) arr.get(i)).estimateSelectivity(Predicate.Op op, (int) v);
else
return ((StringHistogram) arr.get(i)).estimateSelectivity(Predicate.Op op, (String) v);
}
法二:
public static ArrayList<? super Object> arr = new ArrayList<>();
public double estimateSelectivity(Predicate.Op op, String v, int i) {
return ((StringHistogram) arr.get(i)).estimateSelectivity(Predicate.Op op, v);
}
public double estimateSelectivity(Predicate.Op op, int v, int i) {
return ((IntHistogram) arr.get(i)).estimateSelectivity(Predicate.Op op, v);
}
尾声
其实不知道你有没有发现其实不用通配符 ? 也可以实现, 即改成 ArrayList<Object> arr…..
但感觉这样写更帅哈哈哈
不过八当初的空缺弥补了,很开心…..