记录一下泛型的定义,历史,使用案例等。素材主要来源于 On Java 8, Thinking in Java 和 Effective Java。
历史
1.5 版本引入,主要动机是支持 Collection 类
泛型方法
把这一块放到最前面时为了避免理解上的误区,泛型方法和泛型类,泛型接口没有从属关系,就算是普通的 Utils 方法也可以声明泛型方法
1 | public class GenericUtils { |
泛型类
即在声明类时添加类型声明,最常见的如 Collection 系列下的 ArrayList
1 | public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { ... } |
泛型接口
泛型接口只是在接口定义的时候在接口名称后接上类型声明而已。使用 lang 包中自带的 Supplier
接口为例,接口在声明时指定类型,并在 get() 方法中指定返回类型。
1 | // 1.8 中引入的接口,充当工厂方法的角色 |
使用案例
记录一下工作生活中遇到的具体使用案例
代码重构
公司代码重构时遇到下面这种情况:
比如原来有个类叫 Background, 重构时为他抽了一个 interface IBackground。但是在替换一些集合相关的代码时出现了不兼容的问题。
1 | // before |
上面的这种转换失败就是由泛型转化异常造成的
指定泛型返回值为某个类的子类
可以使用泛型方法
1 | public void <T extends Sup> T getSometing() {}; |
返回 Map 类型的泛型方法?
这种用法称为 multi-level wildcards,参考 这篇 文章中的定义
子类现有方法为
1 | public Map<String, List<Sub>> getResult() { |
想要给他一个抽 interface 类似 Map<String, List<? extends Sup>> getResult();
但是会编译错误,需要怎么写?
先说答案,可以使用 Map<String, ? extends List<? extends Sup>> getResult();
这样的语法来适配上面说的这种场景
关于这个问题的几个点:
- List<Sub> 并不是 List<Sup> 的子类,想要表达子类的概念,Java 使用的是
List<? extends Sup>
这样的语法 List<List<?>>
适配所有的参数类型的 listList<? extends List<String>>
适配任何 List 及其子类- 两者结合一下
List<? extends List<?>>
适配任何 list 及其子类,并且适配所有参数类型
Code sample:
1 | public interface Sup { } |
PS: List 前的 ? extends
是不可少的,不然 Override 方法会编译错误,应为实现中是用 ArrayList 这个子类实现的,所以接口定义时语意上要有这个声明
工作中遇到的问题
1 | // 下面两个方法有没有区别? |
没有。。。看了一下这两个方法编译出来的字节码是完全一样的,除了行号。
1 | // access flags 0x401 |
但是这两种表达方式在实现的时候还是有区别的,用界限符(?)的这种,要求在集合类型前面也加上界限符。。。
向 List<? extends Number> 中添加数据失败
List<? extends Number> list = new ArrayList<>(); list.add(3);
向该 list 中添加数据 3 有编译错误。这是应为通过 List<? extends Number> list
声明的 list 可以存储 Number 及其子类,效果上来看下面这些声明的集合只是 ? extends Number
的一部分,那么我们加 3 这个行为在类型一致这个前提下就会有与以上的错误。一般这种声明方式拿到的结果只用于读操作。
1 | List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number |
方法中同时有 Class T 和 T bean 的情况怎么兼容
声明一个 list 类型是 <? extends Number>
我们还有一个方法参数列表 Class<T> clz1, T clz2
这种情况下怎么兼容。
1 | public class TestGeneric { |
语法上就兼容不了,无解,最后通过重新理解 event 系统,根据框架重新安排逻辑绕过了 (; ̄ェ ̄) 等读完泛型相关的章节可以再回头看看,不知道到时会不会有新解
最新消息,上面的有解,只需要在前面加上强转到 Class 类型即可 testGeneric((Class)list.get(0), 1);
资深的还是厉害啊,佩服佩服 (●°u°●) 」不过直接把 Class 类强转就能绕过检测我是没想到,六的飞起。