基本介绍
泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型的出现最引人注目的一个原因,就是为了创造容器类。它的主要目的就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
泛型模式推荐名称
K – 键,比如映射的键。
V – 值,比如 List 和 Set 的内容,或者 Map 中的值。
E – 异常类。
T – 泛型。
为什么要使用泛型:
1. 编译期安全检查,防患于未然。
2. 从集合中取元素不需要进行手工转换,编译器会替你插入隐式的转换。
3. 参数化类型,让类的使用更加灵活。
注意:
虽然你可以将List < String > 传递给类型List的参数,但是不能将它传给类型List< Object >的参数。泛型有子类型化的规则,List< String >是原生态类型List的一个子类型,而不是参数化类型List< Object >的子类型。
几种写法
- 原生态类型 如 List。 移植兼容性 促成了支持原生态类型的决定
- List< Object>是个参数化类型,表示可以包含任何对象类型的一个集合
- 无限制通配符类型如List< ?> 读作‘某个类型的集合’
- 类型限制< T extends Comparable< T>> 读作‘针对可以与自身进行比较的每个类型T’
- 有限制的通配符类型< ? extends E>和< ? super E>
泛型方法
一个基本的原则是:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛化,那么应该有限采用泛型方法。下面来看一个简单的泛型方法的定义:
public class Main {
public static <T> void out(T t) {
System.out.println(t);
}
public static void main(String[] args) {
out("findingsea");
out(123);
out(11.11);
out(true);
}
}
使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。
需要注意,一个static方法,无法访问泛型类的类型参数,所以,若要static方法需要使用泛型能力,必须使其成为泛型方法。
类型推导
泛型方法的一个显著特征是,无需明确指定类型参数的值,不像调用泛型构造器的时候是必须指定的。编译器通过检查方法参数的类型来计算类型参数的值。
利用泛型方法调用所提供的类型推导,使创建参数化类型实例的过程变得更加轻松。
public static <K,V> HashMap<K,V> newHashMap(){
return new HashMap<K,V>();
}
可以用下面的简介代码来取代重复的声明:
Map<String,List<String>> anagrams = newHashMap();
类型擦除
public class GenericTest {
public static void main(String[] args) {
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2);//true
}
}
ArrayList< String>和ArrayList< Integer>很容易被认为是不同的类型。但上面的程序会认为它们是相同的类型。
在泛型代码内部,无法获得任何有关泛型参数类型的信息。
Java泛型是使用擦除来实现的,这意味着在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此ArrayList< String>和ArrayList< Integer>在运行时事实上是相同的类型。这两种形式都被擦除成它们的“原生”类型 – List。
边界
边界使得你可以在用于泛型的类型参数上设置限制条件,但是其潜在的重要的效果是你可以按照自己的边界类型来调用方法。这点在使用框架的时候,设计的模板类中应用较多。
public class ServiceImpl<M extends BaseMapper<T,I>, T, I> implements BaseService<T,I>{
@Autowired
protected M baseMapper;
public int insert(T t) {
return baseMapper.insert(t);
}
public int deleteById(I id) {
return baseMapper.deleteById(id);
}
public int update(T t) {
return baseMapper.update(t);
}
public T selectById(I id) {
return baseMapper.selectById(id);
}
public List<T> selectList() {
return baseMapper.selectList();
}
}
有了这个边界之后,可以调用边界类型里面定义的方法。
PECS
Producer Extends Consumer Super:
频繁往外读取内容的,相当于生产者,适合用上界Extends.
经常往里插入的,相当于消费者,适合用下界Super.