【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)

泛型(Generics)

  • 泛型(Generics)
  • 泛型类型(Generic Type)
    • 多个类型参数
    • 泛型类型的继承
  • 原始类型(Raw Type)
  • 泛型方法(Generic Method)
    • 泛型方法 - 类型推断
    • 泛型方法 – 构造方法
    • 限制类型参数
      • 限制类型参数 — 接收泛型数组返元素最大值方法
      • 限制类型参数 — 要求传入的类型必须可比较
  • 通配符(Wildcards)
    • 通配符 — 上界(`extends`)
    • 通配符 — 下界(`super`)
    • 通配符 — 无限制
    • 通配符 – 继承
    • 通配符 — 注意点
  • 泛型的使用限制

Java笔记目录可以点这里:Java 强化笔记(适合有基础的童鞋,不适合小白)

泛型(Generics)

从 Java 5 开始,增加了泛型技术

什么是 泛型

  • 类型变为参数,提高代码复用率

建议的类型参数名称:
• T :Type
• E :Element
• K :Key
• N :Number
• V :Value
• S、U、V :2nd, 3rd, 4th types

泛型类型(Generic Type)

什么是泛型类型?

  • 使用了泛型的类或者接口
    比如:java.util.Comparatorjava.util.Comparable
public class Student <T> {
	private T score;
	public T getScore() {
		return score;
	}
	public void setScore() {
		this.score = score;
	}
}
// Java 7 以前的写法
Student<String>	stu = new Student<String>();

// Java 7开始, 可以省略右边<>中的类型(钻石语法)
Student<String> stu1 = new Student<>();
stu1.setScore("A");
String score1 = stu1.getScore();

Student<Double> stu2 = new Student<>();
stu2.setScore(98.5);
Double score2 = stu2.getScore();

多个类型参数

public class Student <N, S> {
	private N no;
	private S score;
	public Student(N no, S score) {
		this.no = no;
		this.score = score;
	}
}
Student<String, String> s1 = new Student<>("E9527", "A++");
Student<Integer, Double> s2 = new Student<>(17210224, 96.5);

泛型类型的继承

如果没有泛型类型的类之间有继承关系加了泛型类型不一定会保持继承关系
例如 Integer 类继承自 Number,但是 Box 不继承 Box
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第1张图片
只有泛型类型相同时,原本有继承关系的类会继续保持继承关系
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第2张图片
如果拥有多个泛型类型第一个泛型类型父类的泛型类型相同,即可保持继承关系。
例如,下图中 MyListList 保持了继承关系。
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第3张图片

原始类型(Raw Type)

【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第4张图片

泛型方法(Generic Method)

什么是泛型方法?

  • 使用了泛型的方法(实例方法、静态方法、构造方法),比如 Arrays.sort(T[], Comparator)
public class Main {
	public static void main(String[] args) {
		Student<String, String> s1 = new Student<>();
		// 可以像下面这么写, 但一般不会写的这么麻烦
		Main.<String, String>set(s1, "K99", "C++");
		
		Student<Integer, Double> s2 = new Student<>();
		// 编译器可以自动推断出类型参数的具体类型
		set(s2, 25, 99.5);
	}
	
	// 泛型方法(静态方法)
	static <T1, T2> void set(Student<T1, T2> stu, T1 no, T2 score) {
		stu.setNo(no);
		stu.setScore(score);
	}
}

泛型方法示例:

public class Box<E> {
	private E element;
	public Box() {}
	public Box(E element) {
		this.element = element;
	}
}
public class Main {
	public static void main(String[] args) {
		List<Box<Integer>> boxes = new ArrayList<>();
		addBox(11, boxes);
		addBox(22, boxes);
		addBox(33, boxes);
	}
	// 泛型方法(静态方法)
	static <T> void addBox(T element, List<Box<T>> boxes) {
		Box<T> box = new Box<>(element);
		boxes.add(box);
	}
}

泛型方法 - 类型推断

【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第5张图片
上图是 jdk 中 Collections 类的源码,可以看出 emptyList() 是泛型方法,可以自动推断类型

// 根据接收的类是 List 推断出了泛型是 String 类型
List<String> list1 = Collections.emptyList();
// 根据接收的类是 List 推断出了泛型是 Integer 类型
List<Integer> list2 = Collections.emptyList();

泛型方法 – 构造方法

【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第6张图片

限制类型参数

可以通过 extends 对类型参数增加一些限制条件,比如

  • extends 后面可以跟上类名接口名,代表 T 必须是 A 类型,或者继承、实现 A
//  表示传入的类型参数必须是Numbe及其子类
public class Person<T extends Number> {
	private T age;
	public <E> Person(T age) {
		this.age = age;
	}
	public int getAge() {
		// 传入null则视为0, 否则转为int
		return (age == null) ? 0 : age.intValue();
	}
}
public static void main(String[] args) {
	Person<Double> p1 = new Person<>(18.7);
	System.out.println(p1.getAge()); // 18
	Person<Integer> p2; // 正常运行, Integer是Number的子类
	// Person p3; // 会报错, 因为String不是Number的子类
}
  • 可以同时添加多个限制,比如 ,代表 T 必须同时满足 A、B、C,只能有一个类,但是可以有多个接口类必须放在前面

限制类型参数 — 接收泛型数组返元素最大值方法

public class Main {
	public static void main(String[] args) {
		Double[] ds = {5.6, 3.4, 8.8, 4.6};
		System.out.println(getMax(ds)); // 8.8
		
		Integer[] is = {4, 19, 3, 28, 56};
		System.out.println(getMax(is)); // 56
		}
	
	static <T extends Comparable<T>> T getMax(T[] array) {
		if (array == null || array.length == 0) return null;
		T max = array[0];
		for (int i = 0; i < array.length; i++) {
			if (array[i] == null) continue;
			if (array[i].compareTo(max) <= 0) continue;
			max = array[i];
		}
		return max;
	}
}

限制类型参数 — 要求传入的类型必须可比较

public class Student <T extends Comparable<T>> implements Comparable<Student<T>> {
	private T score; // 传入的 T 继承了 Comparable, 表示是可比较的
	public Student(T score) {
		this.score = score;
	}
	@Override
	public int compareTo(Student<T> s) {
		// 传入若为null, 必然比本身小
		if (s == null) return 1;
		// 若自身的score不为null, 则调用compareTo与传入的进行比较
		if (score != null) return score.compareTo(s.score); 
		// 此时, 自身的score为null, 若传入的score也为null则视为相同, 否则就是传入的大
		return s.score == null ? 0 : -1;
	}
	@Override
	public String toString() {
		return "Student [score=" + score + "]";
	}
}
public class Main {
	public static void main(String[] args) {
		Student<Integer>[] stus = new Student[3];
		stus[0] = new Student<>(18);
		stus[1] = new Student<>(38);
		stus[2] = new Student<>(28);
		// Student [score=38]
		System.out.println(getMax(stus));
 	}
	static <T extends Comparable<T>> T getMax(T[] array) {
		if (array == null || array.length == 0) return null;
		T max = array[0];
		for (int i = 0; i < array.length; i++) {
			if (array[i] == null) continue;
			if (array[i].compareTo(max) <= 0) continue;
			max = array[i];
		}
		return max;
	}
}

通配符(Wildcards)

  • 在泛型中,问号 ? 被称为是通配符
  • 通常用作变量类型返回值类型类型参数
  • 不能用作泛型方法调用、泛型类型实例化、泛型类型定义的类型参数

通配符 — 上界(extends

  • 可以通过 extends 设置类型参数的上界

基本使用:

// 类型参数必须是Number类型或者是Number的子类型
void testUpper(Box<? extends Number> box) {}
// Integer是Number的子类
Box<Integer> p1 = null; 
testUpper(p1); // 可以

// Number类型可以作为参数
Box<Number> p2 = null;
testUpper(p2); // 可以

// ? extends Number 表示参数是Number的子类
Box<? extends Number> p3 = null; // 可以
testUpper(p3);

// ? extends Integer 表示参数是Integer的子类, Integer是Number的子类
Box<? extends Integer> p4 = null; // 可以
testUpper(p4);

// String不是Number的子类, 传入testUpeer会报错
Box<String> p5 = null;
// testUpper(p5); // 报错

示例:对泛型数组的元素求和方法(传入的必须是Number的子类型)

public class Main {
	public static void main(String[] args) {
		List<Integer> is = Arrays.asList(1, 2, 3);
		System.out.println(sum(is)); // 6.0
		
		List<Double> ds = Arrays.asList(1.2, 2.3, 3.5);
		System.out.println(sum(ds)); // 7.0
 	}

	static double sum(List<? extends Number> list) {
		double s= 0.0;
		for (Number n : list) {
			s += n.doubleValue();
		}
		return s;
	}
	
}

通配符 — 下界(super

  • 可以通过 super 设置类型参数的下界

基本使用:

// 类型参数必须是Integer类型或者是Integer的父类型
void testLower(Box<? super Integer> box) {
Box<Integer> p1 = null;
testLower(p1);

Box<Number> p2 = null;
testLower(p2);

Box<? super Integer> p3 = null;
testLower(p3);

Box<? super Number> p4 = null;
testLower(p4);

示例:往泛型数组中添加元素(传入的必须是Integer的父类型)

public class Main {
	public static void main(String[] args) {
		List<Integer> is = new ArrayList<>();
		addNumbers(is); 
		System.out.println(is); // [1, 2, 3, 4, 5]
		
		List<Number> ns = new ArrayList<>();
		addNumbers(ns);
		System.out.println(ns); // [1, 2, 3, 4, 5]
 	}
	
	static void addNumbers(List<? super Integer> list) {
		for (int i = 1; i <= 5; i++) {
			list.add(i);
		}
	} 
	
}	

通配符 — 无限制

// 类型参数是什么类型都可以
void test(Box<?> box) {}
Box<Integer> p1 = null;
Box<Integer> p2 = null;
Box<Integer> p3 = null;
Box<? extends Number> p4 = null;
Box<? super String> p5 = null;
Box<?> p6 = null;

test(p1);
test(p2);
test(p3);
test(p4);
test(p5);
test(p6);

示例:打印泛型数组的元素(无限制,任何类型都可以)

public class Main {
	public static void main(String[] args) {
		List<Integer> is = Arrays.asList(1, 2, 3);
		printList(is); // 1 2 3
		
		List<Double> ds = Arrays.asList(1.2, 2.3, 3.5);
		printList(ds); // 1.2 2.3 3.5
		
 	}
	
	static void printList(List<?> list) {
		for (Object object : list) {
			System.out.print(object + " ");
		}
		System.out.println();
	}
}

通配符 – 继承

无限制的绝对是最顶层的,其余的按正常继承关系理解即可。
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第7张图片

通配符 — 注意点

【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第8张图片

泛型的使用限制

这里列出一些常用的误区,不必死记,过一遍理解即可。
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第9张图片
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第10张图片
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第11张图片
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第12张图片
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第13张图片
【Java 泛型】泛型(泛型类型、原始类型、泛型方法)、通配符(上界、下界、无限制、继承)_第14张图片

你可能感兴趣的:(Java)