目录
一、泛型概述
-
Java泛型是JDK5中引入的一个新特性,提供了编译时类型安全检查机制-----编译时检测到非法的类型。
-
早期的时候,使用Object来代表任意类型。这样在向上转型的是没有问题,但是在向下转型时存在类型转换的问题,这样程序不安全。Java在IDK5之后提供泛型来解决这个问题。
-
泛型的本质是参数化类型,也就是说操作的数据类型被指定为一个参数,
-
泛型是一种,把类型的明确工作推迟到创建对象或者调用方法的时候才去明确的特殊类型。
注意:类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
泛型可以使用在 方法、接口、类中------------泛型类、泛型方法、泛型接口。
二、定义泛型类
-
格式:修饰符 class 类名<类型>{}
-
范例:public class Mayikt<T>{}
-
此处T可以随便写为任意标识,T、E、K、V等形式的参数常用于表示泛型。
package com.dev.springBootDemo.generics;
/**
* 泛型类
* @author zhumq
* @date 2024/7/7 12:54
*/
public class Student<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
package com.dev.springBootDemo.generics;
/**
* 测试泛型
* @author zhumq
* @date 2024/7/7 12:56
*/
public class TestGenerics {
public static void main(String[] args) {
Student<String> student = new Student<>();
student.setT("zhumq");
System.out.println(student.getT()); // zhumq
Student<Integer> student1 = new Student<>();
student1.setT(1);
System.out.println(student1.getT()); // 1
Student<Double> student2 = new Student<>();
student2.setT(1.0);
System.out.println(student2.getT()); // 1.0
Student student3 = new Student<>(); // 没有指定类型,默认为Object,注意类型转化
}
}
三、定义泛型方法
-
格式:修饰符<类型>返回值类型 方法名(类型 变量名){..}
-
范例:public<T>T show(T t){...}
package com.dev.springBootDemo.generics2;
/**
* 泛型方法
* @author zhumq
* @date 2024/7/7 13:02
*/
public class Method {
/**
* <T> 该方法使用泛型
* T 返回值
*/
public <T> T show(T t) {
return t;
}
}
package com.dev.springBootDemo.generics2;
/**
* @author zhumq
* @date 2024/7/7 13:10
*/
public class TestGenerics {
public static void main(String[] args) {
Method m = new Method();
System.out.println(m.show("zhumq")); // zhumq
System.out.println(m.show(1)); // 1
System.out.println(m.show(1.1)); // 1.1
}
}
四、定义泛型接口
-
格式:修饰符interface 接口名<类型>{..}
-
范例:public interface Mayiktlnterface <T>....}
-
实现类也要有<T>
package com.dev.springBootDemo.generics3;
/**
* @author zhumq
* @date 2024/7/7 13:15
*/
public interface Interface<T> {
T show(T t);
}
package com.dev.springBootDemo.generics3;
/**
* @author zhumq
* @date 2024/7/7 13:16
*/
public class InterfaceImpl<T> implements Interface<T> {
@Override
public T show(T t) {
return t;
}
}
package com.dev.springBootDemo.generics3;
/**
* @author zhumq
* @date 2024/7/7 13:14
*/
public class TestGenerics {
public static void main(String[] args) {
Interface<String> i = new InterfaceImpl<>();
System.out.println(i.show("hello")); // hello
Interface<Integer> i2 = new InterfaceImpl<>();
System.out.println(i2.show(1)); // 1
}
}
四、List接口中的泛型是如何定义的
package com.dev.springBootDemo.generics4;
/**
* List中的泛型
* @author zhumq
* @date 2024/7/7 13:25
*/
public interface MyList<E> {
void add(E e);
E get(int index);
}
package com.dev.springBootDemo.generics4;
/**
* @author zhumq
* @date 2024/7/7 13:26
*/
public class MyListImpl<E> implements MyList<E> {
@Override
public void add(E e) {
System.out.println("新增成功!!!");
}
@Override
public E get(int index) {
System.out.println("获取成功!!!");
E e = null;
return e;
}
}
package com.dev.springBootDemo.generics4;
/**
* @author zhumq
* @date 2024/7/7 13:28
*/
public class TestGenerics {
public static void main(String[] args) {
MyList<String> myList = new MyListImpl<>();
myList.add("hello");
}
}
五、泛型通配符
-
类型通配符<?>一般用于接受使用
-
List<?>:表示元素是未知类型的list,可匹配任何类型
-
带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
-
类型通配符上限:<?extends 类型> List<? extends MaviktParent>:它表示的类型是MaviktParent或其子类型
-
类型通配符下限:<? super 类型> List<? super MayiktParent>:它表示的类型是MayiktParent或其父类型
package com.dev.springBootDemo.generics5;
import javax.xml.transform.sax.SAXTransformerFactory;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 通泛型配符
* @author zhumq
* @date 2024/7/7 13:32
*/
public class TestGenerics {
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList<>();
for (int i = 0 ; i < 5 ; i++) {
stringList.add("i:" + i);
}
printList(stringList);
// ArrayList<Integer> integerList = new ArrayList<>();
// printList(integerList);
}
// 不能add操作,可以get,获取类型为Object
public static void printList (List<?> list) {
Iterator<?> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
六、 泛型通配符的上限和下限
package com.dev.springBootDemo.generics6;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhumq
* @date 2024/7/7 13:46
*/
public class TestGenerics {
public static void main(String[] args) {
ArrayList<Parent> parents = new ArrayList<>();
ArrayList<Chlidren> children = new ArrayList<>();
printList(parents);
printList(children);
ArrayList<Object> objects = new ArrayList<>();
printList2(objects);
}
/**
* 类型通配符上限
* List<? extends Parent> 上限, 表示list中存放的元素类型是Parent或者Parent的子类
*
* @param list
*/
public static void printList(List<? extends Parent> list){}
/**
* 类型通配符下限
* List<? super Parent> 下限, 表示list中存放的元素类型是Parent或者Parent的父类
*
* @param list
*/
public static void printList2(List<? super Parent> list){}
}
七、可变参数
-
可变参数:又称参数个数可变,用作方法的形参出现,那么方法参数个数就是 可变的了。
-
书写格式
-
修饰符 返回值类型 方法名(数据类型...量名){)
-
范例:public static int sum(int... a){}
-
-
可变参数注意事项
-
这里的可变参数变量其实是一个数组。
-
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
-
package com.dev.springBootDemo.generics7;
/**
* @author zhumq
* @date 2024/7/7 14:05
*/
public class TestGenerics {
public static void main(String[] args) {
System.out.println(sum(1, 2, 3, 4, 5));
}
/**
* 可变参数 ,底层是数组
* 传递的参数列表是一个数组
*/
public static int sum (int b,int ...a) {
// a做累加处理,减去b 2-5累加 减去1
int sum = 0;
for (int i = 0; i < a.length; i++) {
sum += a[i];
}
sum -= b;
return sum;
}
}
八、可变参数的使用
package com.dev.springBootDemo.generics8;
import java.util.Arrays;
import java.util.List;
/**
* @author zhumq
* @date 2024/7/7 14:14
*/
public class TestGenerics {
public static void main(String[] args) {
// 定义的元素个数是不能改变的,可以修改数据
List<String> list = Arrays.asList("张三", "李四", "王五");
// list.remove(0);// 不能删除
// list.add("赵六");// 不能添加
list.set(0, "赵六");
System.out.println(list);
}
}
九、泛型擦除机制
-
泛型底层底层原理,使用擦除机制
-
泛型在编译阶段限制传递的类型,意味着在编译阶段,所有泛型类型信息都会被从字节码中移除(擦除),只留下原始类型
-
在运行的时候是没有泛型的,可以对.class文件进行反编译进行观察
package com.dev.springBootDemo.generics9;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhumq
* @date 2024/7/7 14:22
*/
public class TestGenerics {
// public static void main(String[] args) {
// ArrayList<String> strings = new ArrayList<>();
// strings.add("张三");
// // 泛型擦除:将一个指定的泛型类型赋值给一个没有指定泛型类型的变量,就会发生泛型擦除
// ArrayList arrayList = strings;
// arrayList.add(1);
// }
public static void main(String[] args) {
// 创建一个泛型列表,只允许存放 String 类型
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
Class<? extends List> aClass = stringList.getClass();
// 创建一个泛型列表,只允许存放 Integer 类型
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
Class<? extends List> aClass1 = integerList.getClass();
// 检查两个列表的类型是否相同
System.out.println(stringList.getClass() == integerList.getClass()); //true
}
}
/*这表明尽管我们在编译时指定了不同的泛型参数(String 和 Integer),但在运行时,stringList 和 integerList 都被识别为 java.util.ArrayList 类的实例,而不是 ArrayList<String> 或 ArrayList<Integer>。
这是因为编译器在编译阶段会执行类型擦除,将所有的泛型类型转换为它们的原始类型。在这个例子中,List<String> 和 List<Integer> 都被转换为 List,并且 ArrayList<String> 和 ArrayList<Integer> 都被转换为 ArrayList。*/