本文讲述泛型的概念、泛型的应用场景,让我们更了解泛型的作用
目录
一、泛型的概念
1、什么是泛型
2、泛型的好处
3、泛型的相关名词
4、类型变量
的应用场景
二、泛型的应用场景
1、泛型类与泛型接口
2、泛型方法
3、类型通配符
3.1、为什么需要通配符
3.2、作用
3.3、类型通配符的分类
3.4、上限通配符
3.5、下限通配符
三、可变参数
1、格式
2、可变参数的应用场景
总结
一、泛型的概念
1、什么是泛型
例如:生产瓶子的厂家,一开始并不知道我们将来会用瓶子装什么,我们什么都可以装,但是有的时候,我们在使用时,想要限定某个瓶子只能用来装什么,这样我们不会装错,而用的时候也可以放心的使用,无需再三思量。我们生活中是在使用这个瓶子时在瓶子上“贴标签”,这样就轻松解决了问题。
泛型是JDK 5.0引入的特性,允许在定义类、接口或方法时使用类型参数。泛型的核心目的是提高代码复用性,同时增强类型安全性和减少强制类型转换的需要。
例如:java.lang.Comparable接口和java.util.Comparator接口,是用于对象比较大小的规范接口,这两个接口只是限定了当一个对象大于另一个对象时返回正整数,小于返回负整数,等于返回0。但是并不确定是什么类型的对象比较大小,之前的时候只能用Object类型表示,使用时既麻烦又不安全,因此JDK1.5就给它们增加了泛型。
public interface Comparable<T>{ int compareTo(T o) ; }
public interface Comparator<T>{ int compare(T o1, T o2) ; }
其中<T>就是类型参数,即泛型。
2、泛型的好处
public class GenericInterfaceTest {
public static void main(String[] args) {
Object o = new GenericImpl1().show("2");
System.out.println(((String)o).length());//强转
// new GenericImpl2().show("2")//报错
Integer show = new GenericImpl2().show(2);//无需强转
}
}
interface GenericInterface<T>{
T show(T t);
}
class GenericImpl1 implements GenericInterface{//无限定类型
@Override
public Object show(Object o) {
return o;
}
}
class GenericImpl2 implements GenericInterface<Integer>{///限定类型为Integer
@Override
public Integer show(Integer integer) {
return integer;
}
}
-
类型安全:编译时检查类型,避免运行时 ClassCastException
-
代码简洁:无需强制类型转换
-
示例对比:
-
无泛型:需要 (String) o强制转换
-
有泛型:直接使用 Integer o,编译器确保类型正确
-
3、泛型的相关名词
| 类型变量(Type Variables) | 代表未知的数据类型 |
<T>, <E>,<K,V>等 |
| 参数化类型(Parameterized Types) | 已指定类型参数的类/接口 | Comparator<String> |
| 泛化的数组类型(GenericArrayType) | 代表未知的数组类型 | T[ ] |
| 通配符类型(WildcardType) | 表示未知类型 | Comparator<?> |
4、类型变量<T>的应用场景
- 声明类或接口时,在类名或接口名后面声明类型变量,我们把这样的类或接口称为泛型类或泛型接口
【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{ } 【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{ }
例如: public class ArrayList<E> public interface Map<K,V>{ …. }
- 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是<font color='red'>声明</font>不是单纯的使用)了类型变量的方法称为泛型方法
【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{ //… }
例如:java.util.Arrays类中的 public static <T> List<T> asList(T… a){ …. }
二、泛型的应用场景
1、泛型类与泛型接口
泛型类格式:
修饰符 class 类名<类型> { }
泛型接口格式:
修饰符 interface 接口名<类型> { }
自从JDK1.5引入泛型的概念之后,对之前核心类库中的API做了很大的修改,例如:集合框架集中的相关接口和类、java.lang.Comparable接口、java.util.Comparator接口、Class类等等。
下面以Collection、ArrayList集合以及Iterator迭代器为例演示,泛型类与泛型接口的使用。
import java.util.Arrays;
public class TestRectangle {
public static void main(String[] args) {
Rectangle[] arr = new Rectangle[4];
arr[0] = new Rectangle(6,2);
arr[1] = new Rectangle(4,3);
arr[2] = new Rectangle(12,1);
arr[3] = new Rectangle(5,4);
System.out.println("排序之前:");
for (Rectangle rectangle : arr) {
System.out.println(rectangle);
}
Arrays.sort(arr);
System.out.println("排序之后:");
for (Rectangle rectangle : arr) {
System.out.println(rectangle);
}
}
}
class Rectangle implements Comparable<Rectangle>{
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double area(){
return length * width;
}
public double perimeter(){
return 2 * (length + width);
}
@Override
public String toString() {
return "Rectangle{" +
"length=" + length +
", width=" + width +
",area =" + area() +
",perimeter = " + perimeter() +
'}';
}
@Override
public int compareTo(Rectangle o) {
int compare = Double.compare(area(), o.area());
return compare != 0 ? compare : Double.compare(perimeter(),o.perimeter());
}
}
2、泛型方法
格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名){ }
public class GenericMethodTest {
public static void main(String[] args) {
//不使用泛型
String[] strArr = {"孙行者","者行孙","行者孙"};
Object e = new GenericMenthod().getElementNoGeneric(strArr,2);
System.out.println(e);
System.out.println(((String)e).length());
// 使用泛型
Integer[] intArr = {2,6,1};
Integer element = new GenericMenthod().getElement(intArr, 1);
System.out.println(element);
}
}
class GenericMenthod{
/**
* 返回指定索引位置元素
*/
public Object getElementNoGeneric(Object[] array,int index){
return array[index];
}
/**
* 返回指定索引位置元素
*/
public <T> T getElement(T[] array,int index){
return array[index];
}
}
3、类型通配符
3.1、为什么需要通配符
List<Number> list = new ArrayList<Integer>(); //编译错误:泛型不支持多态
3.2、作用
为了表示各种泛型List的父类,可以使用类型通配符
3.3、类型通配符的分类
- 类型通配符:<?>
List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
- 类型上限通配符:<? extends 类型>
List<? extends Number>:它表示的类型是Number或者其子类型
- 类型下限通配符:<? super 类型>
List<? super Number>:它表示的类型是Number或者其父类型
案例:
public class GenericTest {
public static void main(String[] args) {
System.out.println("———-类型通配符:<?>");
List<?> list1 = new ArrayList<Object>();
List<?> list2 = new ArrayList<Number>();
List<?> list3 = new ArrayList<Integer>();
//list1.add(3);//编译不通过
}
}
3.4、上限通配符
List<? extends Number> list = new ArrayList<Integer>();
import java.util.ArrayList;
import java.util.List;
public class GenericExtendsTest {
public static void main(String[] args) {
List<? extends Number> list = new ArrayList<>();
// list.get(1);//编译通过
// list.add(1);//编译不通过,泛型代表确定的类型,?代表不确定类型
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(3);
printElement(list1);
}
//该方法能接收的参数:Number的子类
private static void printElement(List<? extends Number> list) {
for (Number number : list) {
System.out.println(number);
}
}
}
3.5、下限通配符
List<? super Number> list = new ArrayList<Integer>();
import java.util.ArrayList;
import java.util.List;
public class GenericSuperTest {
public static void main(String[] args) {
List<? super Integer> list = new ArrayList<>();
// list.add(1);//编译通过
// Object object = list.get(1);//读不太好用
List<Integer> list1 = new ArrayList<>();
addElement(list1,2);
System.out.println(list1);
List<? super Integer> list2 = new ArrayList<>();
addElement1(list2,2);
System.out.println(list2);
}
//list接收的数据类型(>=Integer)
private static void addElement(List<? super Integer> list, Integer e) {
list.add(e);
}
//list接收数据类型(>=T)
private static<T> void addElement1(List<? super T> list1, T e) {
list1.add(e);
}
}
| 读取能力 | 写入能力 | 主要用途 | 类型安全 | |
| 上限通配符<? extends T> | ✅ 可安全读取为 T 类型 |
❌ 基本不能写入 (只能写null) |
1. 从集合获取元素处理 2. 只读操作 3. 生产数据供使用 |
读取安全 |
| 下限通配符<? super T> |
❌ 只能读取为 Object 类型 |
✅ 可写入 T 及其子类 |
1. 向集合添加元素 2. 写入操作 3. 消费数据 | 写入安全 |
三、可变参数
1、格式
修饰符 返回值类型 方法名 ( 数据类型… 变量名){ }
注意:
(1)这里的变量其实是一个数组
(2)如果一个方法有多个参数,包含可变参数,可变参数要放在最后
案例:
public class ParamTest {
public static void main(String[] args) {
System.out.println(sum());
System.out.println(sum(10));
System.out.println(sum(10, 20));
System.out.println(sum(10, 20, 30));
System.out.println(sum(10, 20, 30, 40));
int[] intArr = {10, 20, 30, 40};
System.out.println(sum(intArr));
}
public static int sum(int… a) {
int sum = 0;
for (int i : a) {
sum += i;
}
return sum;
}
/*public static int sum(int… a, int b) {
return 0;
}*/
}
2、可变参数的应用场景
- Arrays工具类:
public static List asList(T… a):返回由指定数组支持的固定大小的列表
总结
-
泛型本质:类型参数化,安全又灵活
-
通配符原则:
-
extends:能取不能存
-
super:能存不能取
-
<?>:只读不写
-
-
可变参数:类型后加…,必须在最后
-
PECS原则:Producer Extends, Consumer Super
网硕互联帮助中心






评论前必须登录!
注册