泛型使用场景
1 编译是强类型检查
在编译的时候指定了泛型类型,比如向集合里面添加元素的时候会进行类型检查。
2 避免类型转换
// 如果这样的话在编译的时候不会有任何问题,但是如果是运行的时候可能就会报错
public static void merge(List a, List b) {
a.addAll(b);
}
3 实现通用算法
比如集合里面元素E,使用泛型实现了通用方法。
设计贴士:
public interface Type {
// 为什么要提供默认实现呢?因为要兼容老版本,不然会找不到这个方法 所以是为了兼容老版本
default String getTypeName() {
return toString();
}
}
泛型命令规约
E:表示集合元素
K:表示键
V:表示值
T:表示类型
可以参考Function下面包的设计。
泛型的有界性参数
1 申明上限
public class T {
public static void main(String[] args) {
// 虽然限制了上界,但是仍然存在问题
// 由于擦写以后都是 Object
// StringBuffer String 都是 CharSequence的子类
Container<String> c1 = new Container<>("");
Container<StringBuffer> c2 = new Container("demo");
// 输出的是demo 因为构造器传入的类型都是String 运行时都是Object 所以没有问题
System.out.println(c2.getEle());
// 报错
StringBuffer ele = c2.getEle();
// 也只能传入 StringBuffer 的参数
c2.set()
}
static class Container<E extends CharSequence> {
private E ele;
public Container(E e) {
this.ele = e;
}
public boolean set(E e) {
this.ele = e;
return true;
}
public E getEle() {
return ele;
}
}
}
Java 泛型对象操作的时候,看申明对象类型参数
2 多界限泛型参数类型
多参数限制类型 extends 第一个类型可以是类,也可以是接口
第二或更多的参数必须时接口
public class TT {
public static class C {
}
public static interface T1 {
}
public static interface T2 {
}
/**
* 多参数限制类型 extends 第一个类型可以是类,也可以时接口
* 第二或更多的参数必须时接口
* @param <T>
*/
public static class Demo<T extends C & T1 & T2> {
}
}
3 泛型的方法
public static <E> void addE(Collection<E> target, E element) {
target.add(element);
}
public static void main(String[] args) {
// 由于擦写以后全是Object 所以可以这样写但是肯定不能这样用
addE(new ArrayList<>(), "string");
addE(new ArrayList<>(), 1);
}
// 为了通用我们可以限定 的更抽象
public static <C extends Iterator<E>, E extends Serializable> void forEach(Collection<E> source, Consumer<E> consumer) {
for (E e : source) {
consumer.accept(e);
}
}
public static void main(String[] args) {
forEach(Arrays.asList(1, 2, 3), System.out::println);
}
4 通配符设计
/**
* 如果前面为通配符,后面只能为Object
* @param list
* @param consumer
*/
public static void forEach(List<? extends Number> list, Consumer<Object> consumer) {
// 这里是object类型了
for (Object e : list) {
consumer.accept(e);
}
}
public static void main(String[] args) {
List<Number> numbers = new ArrayList<>();
numbers.add(Byte.valueOf((byte) 1));
numbers.add(Short.valueOf((short) 2));
numbers.add(Integer.valueOf(3));
numbers.add(Long.valueOf(4));
List<Byte> bytes = Arrays.asList((byte)5);
List<Short> shorts = Arrays.asList((short)6);
List<Integer> integers = Arrays.asList(7);
// ? extends E 因为addAll这里是通配符类型
numbers.addAll(bytes);
numbers.addAll(shorts);
numbers.addAll(integers);
forEach(numbers, System.out::println);
}
所以我们设计的时候被操作的对象需要为更抽象的类型(Number),待整合的对象可以是具体类型(Byte,Short,Long)
如果不做任何限定:
// 会存在方法签名冲突
public static void print(Iterator<?> iterable) {}
public static void print(Iterator<Object> iterable) {
}
super 与 extend
public static void lower(List<? extends Number> producer, List<? super Number> consumer) {
// 消费数据用 extend 比如我们遍历数据就是消费
// 报错 不允许添加因为不确定与实际类型是否兼容
producer.add(Integer.valueOf(1));
// 操作数据用super
// 可以正常添加
consumer.add(Integer.valueOf(1));
}
通过字节码获取List里面的类型:
private List<Map<String, List<Object>>> list = new ArrayList<>();
public static void main(String[] args) throws NoSuchFieldException {
List<Map<String, List<Object>>> list = new ArrayList<>();
Field field = TT.class.getDeclaredField("list");
ResolvableType type = ResolvableType.forField(field);
System.out.println(type.getGeneric(0));
}