泛型是Java中允许程序员在强校验下定义某些可变部分,以达到代码复用的目的。
一、为什么要使用泛型?
假设我们有一个烤箱,烤箱可以用来烤面包、烤鱼甚至烤红薯。
假设面包、鱼这些食物在Java中都是一个个类,这个些类都继承于Food这个父类。
我们在烤箱类的烘烤方法中使用泛型,传入对应的类的实例就可以实现烘烤它的功能。
二、怎样使用泛型?
1.泛型类
HashMap 就是一个泛型类,通过传入的参数来定义 Key,Value 的数据类型。这里的 Key 和 Value 可以是 Java 已经提供的类型,如: String;也可以是你自己定义的类。
/ *
* <K> – the type of keys maintained by this map
* <V> – the type of mapped values
* /
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
例如,我们可以通过HashMap<String,String>
来定义一个 key 和 value 都是 String 类型的 HashMap。
泛型类中的方法可以直接使用传入的泛型参数 K 和 V,下面的代码是 HashMap 中 get 方法的源码
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
由此可见在泛型类中我们可以直接使用 K 和 V 两个泛型参数,而不用像下面的泛型方法一样还是需要在方法名定义泛型。
2.泛型方法
直接使用泛型方法,下面我们定义的了一个烤的泛型方法,可以传入所有 Food 的子类。
public <T extends Food>void kao(T a){
T foodname=a;
System.out.println(a+"被烤了");
}
可以看到,泛型方法中的泛型 T 需要在方法的返回值(void)前面进行定义,泛型类中的方法不需要这样做。
3.泛型接口
public interface Map<K,V> {
}
上面展示了 Map 接口是如何定义泛型的,泛型接口的定义和泛型类十分相似。这里就不在赘述了。
总结
- 尖括号里的 ? 都指代一种未知类型
- 尖括号的位置非常讲究,必须在类名之后或方法返回值之前
- 泛型在定义处只具备执行Object方法的能力
当然Java语言中的泛型可以说是一种伪泛型,他仅仅是一种编写代码的语法检查,在编译时就可以看到他的原型。下面我们就来讨论一下,什么是伪泛型?
三、泛型原理
我们可以通过查看编译之后的 Java 字节码来查看泛型是如何被实现的。
既然 Java 中的所有类都继承自 Object 类,那么为什么不直接使用 Object 来定义泛型呢?
其实 Java 内部就是这样实现泛型的,Java 编译器会将 T 擦出,使用 Object,并加上必要的类型转换。
而 Java 虚拟机在运行时是完全不知道泛型这回事的。
至于为什么不直接使用 Object 类,本质上还是——如果使用 Object 类来描述泛型的话代码就更复杂了。
其次,使用 Java 泛型可以在编译前就发现问题。
也可以说,Java 为我们提供了一种简单的定义泛型的方式。
参考资料
[1] 《Java编程的逻辑》
[2] 《码出高效——Java开发手册》
[3] 极客时间 《Java核心技术36讲》