Skip to content

[!quote] 泛型 泛型 提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型

  • 泛型的类型参数只能是包装类【Integer ……】,不能是基本类型【int……】

[!quote] 泛型标记符 泛型的标记符可以自己定义,叫什么都可以

java
public static <PO, VO> Page\<VO> of(Page\<PO> pagePO) { …… }
  • E Element 【在集合中使用,因为集合中存放的是元素】
  • T Type【Java 类】
  • K Key【键】
  • V Value【值】
  • N Number【数值类型】

❤ 泛型类,泛型方法,泛型接口

泛型类

  • 实现了一个类中不同类型的成员变量
java
class Generic<T> {               //泛型类
	private T t;         // T 可以指代任何类型
   
	public T getT() {return t;}  
  
	public void setT(T t) {this.t = t;}        //泛型方法
}
java
public static void main(String[] args) {  
	Generic<Integer> gr = new Generic<Integer>();  
	gr.setT(123);  
	System.out.println(gr.getT());  
  
	Generic<String> grr = new Generic<String>();  
	grr.setT("Java");  
	System.out.println(grr.getT());  
}

---
123
Java

泛型方法

  • 实现了同一个方法可以容纳不同的数据类型【方法重载虽然也可以,但代码冗余】
java
public class demo {  
	public <T> void printArray(T[] inputArray) {  
	    // 输出数组元素  
	    for (T element : inputArray) {  
	        System.out.printf("%s ", element);  
	    }  
	}
}

泛型接口

java
public interface Generic<T> {  
	void show(T t);  
}
java
public class GenericImp<T> implements Generic<T> {  
	public void show(T t) {  
		System.out.println(t);  
	}  
}
java
public class Demo1 {  
	public static void main(String[] args) {  
		Generic gr = new GenericImp();    //多态
		gr.show(123);  
		gr.show("Java");  
	}  
}

---
123
Java

❤ 有界的类型参数

有时候,我们希望限制可以传递给类型参数的类型的范围【例如,一个操作数字的方法只希望接受 Number ,或者 Number 子类的实例】

java
// 只有实现了 `Comparable<T>` 接口的类型才能作为类型参数。这样,我们就确保了传递给 `findMax` 方法的数组中的对象可以进行比较
public <T extends Comparable<T>> T findMax(T[] array) {
    T max = array[0];
    for (T element : array) {
        if (element.compareTo(max) > 0) {
            max = element;
        }
    }
    return max;
}

❤ 类型通配符

类型通配符是使用 ? 代替具体的类型参数,List<?> 是所有类似于 List<String>List<Integer> …… 的父类

java
public static void main(String[] args) {  
	//继承关系:Integer extends Number extends Object
    List<?> list1 = new ArrayList<Object>();  
    List<?> list2 = new ArrayList<Number>();  
    List<?> list3 = new ArrayList<Integer>();  

	//extends Number表示上限是Number类
    List<? extends Number> list4 = new ArrayList<Object>();  //报错,不能为Object类
    List<? extends Number> list5 = new ArrayList<Number>();  
    List<? extends Number> list6 = new ArrayList<Integer>();  

	//super Number表示下限是Number类
    List<? super Number> list7 = new ArrayList<Object>();  
    List<? super Number> list8 = new ArrayList<Number>();  
    List<? super Number> list9 = new ArrayList<Integer>();  //报错,不能为Integer类
}

[!hint] ?<T> 的区别

  • <T> 用于定义泛型,而 ? 用于使用泛型

❤ 创建集合时最好加上泛型

不加入泛型

如果没有设置泛型,集合的默认类型是 Object, 当 l.add 添加的是 int 时,程序不会编译错误,但是在遍历强转时会报错【因为无法将 Integer 类型强转为 String 类型,当然,如果你使用 Object o = it.next() 就可以解决,但是不推荐】

java
public static void main(String[] args) {  
    List l = new ArrayList();  
    l.add("你");  
    l.add("123");  
  
    Iterator it = l.iterator();  
    while (it.hasNext()) {  
        String s = (String) it.next();  
        System.out.println(s);  
    }  
}

---
java.lang.ClassCastException
java
public static void main(String[] args) {
	List l = new ArrayList();
	l.add("你");
	l.add(123);

	Iterator it = l.iterator();
	while (it.hasNext()) {
		Object o = it.next();
		System.out.print(o);
	}
}

---
你123

加入泛型

如果设置了泛型,在编译期间 add 一个其他类型的数据就会报错

❤ 可变参数

可变参数 可以让我们实现方法形参的可变

java
public class Demo2 {  
    public static void main(String[] args) {  
        System.out.println(sum(1, 2, 3));  
    }  
  
    public static int sum(int... a) {      //可变参数,这个a其实是个数组
        int sum = 0;  
        for (int i : a) {  
            sum = sum + i;  
        }  
        return sum;  
    }  
}

---
6

如果你的方法里既要有固定参数又要有可变参数的话,要把可变参数写在后面,~~因为如果写在前面,编译器会认为你传的参数全部都是可变参数~~。所以一个方法不能有两个可变参数

❤ 补充

类型擦除

[!quote] 类型擦除 类型擦除 是 JVM 在编译时,对泛型的类型进行擦除【例如,List<String> 会变成 List ,这意味着在运行时,Java 不知道 List 里存的是 String 还是其他类型

[!hint] 以下是关于泛型擦除的报错代码:

java
// 由于泛型擦除,所以最后两个方法名和参数会完全一样,所以会报错
public static void method(List\<String> list) {
	System.out.println("HelloWorldStringlist");
}

public static void method(List\<Long> list) {
	System.out.println("HelloWorldLonglist");
}
java
// T 是泛型,编译器会在编译时把它擦除成 Exception,这样,Java 在运行时就不知道 T 是哪种具体的异常类型
public \<T extends Exception> void handleException() {
    try {
        // 可能会抛出异常的代码
    } catch (T e) {  // 这是非法的
        // 处理异常
    }
}