云计算百科
云计算领域专业知识百科平台

Java基础——泛型

本文讲述泛型的概念、泛型的应用场景,让我们更了解泛型的作用


目录

一、泛型的概念

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

赞(0)
未经允许不得转载:网硕互联帮助中心 » Java基础——泛型
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!