Java 泛型是从 jdk1.5 开始加入的,Java 泛型的实现采取了 “伪泛型” 的策略,即 Java 在语法上支持泛型,但是在编译阶段会进行所谓的 “类型擦除”,将所有的泛型表示 (尖括号中的内容) 都替换为具体的类型 (其对应的原生态类型),就像完全没有泛型一样。
引入泛型的意义在于:
- 适用于多种数据类型执行相同的代码 (代码复用)
private static int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static float add(float a, float b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
private static double add(double a, double b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
}
如果没有泛型,要实现不同类型的方法,每种类型都需要重载一个 add 方法,通过泛型,我们可以复用为一个方法:
private static <T extends Number> double add(T a, T b) {
System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
return a.doubleValue() + b.doubleValue();
}
- 泛型中的类型在使用时指定,不需要强制类型转换 (类型安全,编译器会检查类型)
# 泛型的基本使用
#
- 从一个简单的泛型类看起:
class Point<T>{ // 此处可以随便写标识符号,T是type的简称
private T var ; // var的类型由T指定,即:由外部指定
public T getVar(){ // 返回值的类型由外部决定
return var ;
}
public void setVar(T var){ // 设置的类型也由外部决定
this.var = var ;
}
}
public class GenericsDemo06{
public static void main(String args[]){
Point<String> p = new Point<String>() ; // 里面的var类型为String类型
p.setVar("it") ; // 设置字符串
System.out.println(p.getVar().length()) ; // 取得字符串的长度
}
}
- 多元泛型
class Notepad<K,V>{ // 此处指定了两个泛型类型
private K key ; // 此变量的类型由外部决定
private V value ; // 此变量的类型由外部决定
public K getKey(){
return this.key ;
}
public V getValue(){
return this.value ;
}
public void setKey(K key){
this.key = key ;
}
public void setValue(V value){
this.value = value ;
}
}
public class GenericsDemo09{
public static void main(String args[]){
Notepad<String,Integer> t = null ; // 定义两个泛型类型的对象
t = new Notepad<String,Integer>() ; // 里面的key为String,value为Integer
t.setKey("汤姆") ; // 设置第一个内容
t.setValue(20) ; // 设置第二个内容
System.out.print("姓名;" + t.getKey()) ; // 取得信息
System.out.print(",年龄;" + t.getValue()) ; // 取得信息
}
}
# 泛型接口
- 简单的泛型接口
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
}
class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
}
public class GenericsDemo24{
public static void main(String arsg[]){
Info<String> i = null; // 声明接口对象
i = new InfoImpl<String>("汤姆") ; // 通过子类实例化对象
System.out.println("内容:" + i.getVar()) ;
}
}
# 泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型。
- 定义泛型方法的语法格式
public class Sace {
// <T> 声明此方法持有一个类型T,也可以理解为声明此方法为泛型方法
// T 指明该方法返回对象为T类型
// Class<T> 指明泛型的具体类型
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException {
T t = c.newInstance();
return t;
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Sace sace = new Sace();
Class<Date> aClass = Date.class;
Date object = sace.getObject(aClass);
System.out.println(object);
}
}
# 泛型的上下限
以下代码会报错
class A{}
class B extends A {}
// 如下两个方法不会报错
public static void funA(A a) {
// ...
}
public static void funB(B b) {
funA(b);
// ...
}
// 如下funD方法会报错
public static void funC(List<A> listA) {
// ...
}
public static void funD(List<B> listB) {
funC(listB); // Unresolved compilation problem: The method doPrint(List<A>) in the type test is not applicable for the arguments (List<B>)
// ...
}
为了解决泛型中隐含的转换问题,Java 泛型加入了类型参数的上下边界机制。<? extends A> 表示该类型参数可以是 A (上边界) 或者 A 的子类类型。编译时擦除到类型 A,即用 A 类型代替类型参数。这种方法可以解决开始遇到的问题。
public static void funC(List<? extends A> listA) {
// ...
}
public static void funD(List<B> listB) {
funC(listB); // OK
// ...
}
- 泛型上下限的引入
在使用泛型的时候,我们可以为传入的泛型类型实参进行上下边界的限制,比如:类型实参只准传入某种类型的夫类型或某种类型的子类
上限
public class Info<T extends Number> {
T val;
public T getVal() {
return val;
}
public void setVal(T val) {
this.val = val;
}
public static void main(String[] args) {
Info info = new Info<Double>();
Info info2 = new Info<Integer>();
info.val = 10;
info2.val = 10f;
System.out.println(info.val);
System.out.println(info2.val);
}
}
下限
public class Info<T> {
T val;
public T getVal() {
return val;
}
public void setVal(T val) {
this.val = val;
}
public static void fun(Info<? super String> temp) { // 只能接收String或Object类型的泛型,String类的父类只有Object类
System.out.print(temp + ", ");
}
public static void main(String[] args) {
Info<String> info = new Info<String>();
Info<Object> info2 = new Info<Object>();
Info<Integer> info3 = new Info<Integer>();
fun(info);
fun(info2);
fun(info3); // info3不允许作为fun的形参,info3是Integer泛型对象,需要一个String的父类,包括String类
}
}
总结:
- > 无限制通配符
- extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
- super E> super 关键字声明了类型的下界,表示了参数化的类型可能是指定类型,或者是次类型的父类