什么是泛型?
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:ArrayList<E>、HashMap<K,V>),称为泛型 类、泛型接口,泛型方法、它们统称为泛型。
作用:
泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力!
原理:
使用泛型时会用具体的数据类型替代泛型变量
泛型类、接口的写法:
public class ArrayList<E>{
. . .
}
public interface Comparator<E>{
. . .
}
注意:类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
泛型方法的写法:
public static <T> void test(T t){
}
案例:打印任意类型的数组
要求: 1. 在测试类中,定义一个printArr方法
2. 该方法可以打印任意数据类型的数组
3. 要求使用泛型来代表该方法支持的数据类型
/*
* 测试利用泛型打印任意类型数组
*/
public class printArrTest {
public static void main(String[] args) {
String[] arr = {"a","b","c"};
printArr(arr);
//泛型不支持基本数据类型
// int[] arr1 = {1,2,3};
// printArr(arr1);
System.out.println(" ");
//泛型支持包装类
Integer[] arr1 = {1,2,3};
printArr(arr1);
}
// 泛型方法
public static <T> void printArr(T[] arr){
//遍历数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
案例:自定义MyArrayList类,模拟ArrayList的增删改成方法
要求:
1.自定义一个类MyArrayList,使用泛型限定它能处理的数据类型,在该类中,添加add remove set get方法,方法内的逻辑可以忽略
import java.util.ArrayList;
/*
* 泛型演示
*/
public class MyArrayList<E>{
private ArrayList<E> list = new ArrayList<>();
public void add(E e){
list.add(e);
System.out.println("添加成功");
}
public void remove(int index){
list.remove(index);
System.out.println("删除成功");
}
public void set(int index, E e){
list.set(index, e);
System.out.println("修改成功");
}
public E get(int index){
if(list.get(index) == null){
System.out.println("查询失败");
return null;
}
System.out.println("查询成功");
return list.get(index);
}
}
2.定义测试类,创建MyArrayList时,给泛型变量传入具体的数据类型,调用四个模拟方法
import java.util.ArrayList;
public class MyArrayListTest {
public static void main(String[] args) {
test1();
System.out.println("--------------------------");
test2();
}
public static void test1(){
//创建集合
MyArrayList list1 = new MyArrayList();
list1.add("张三");
list1.add(123);
list1.add(true);
list1.remove(1);
list1.set(1,"王二");
System.out.println(list1.get(1));
//泛型, 指定集合中存储的数据类型为 String
MyArrayList<String> list2 = new MyArrayList<>();
list2.add("李四");
// list2.add(123);
// list2.add(true);
}
public static void test2(){
// 创建集合, 指定集合中存储的数据类型为 String
ArrayList<String> list1 = new ArrayList<>();
list1.add("张三");
list1.add("李四");
list1.add("王五");
list1.add("赵六");
//遍历集合
list1.forEach(s -> System.out.print(" "+s));
System.out.println(" ");
System.out.println("------------------------------");
//删除集合
list1.remove(2);
list1.forEach(s -> System.out.print(" "+s));
System.out.println(" ");
System.out.println("------------------------------");
//修改集合
list1.set(0,"Hello world");
list1.forEach(s -> System.out.print(" "+s));
System.out.println(" ");
System.out.println("------------------------------");
//获取集合中的数据
System.out.println(list1.get(0));
System.out.println(list1.get(1));
}
}
通配符和上下限:
通配符 :就是 “?” ,可以在“使用泛型”的时候代表一切类型; E T K V 是在定义泛型的时候使用。
泛型的上下限:
泛型上限: : ? 代表能接收的必须是Car或者其子类 。
泛型下限: : ? 代表能接收的必须是Car或者其父类。
案例: 要求:
1. 定义一个方法printList,该方法的形参可以接收Animal类型的集合
2. 测试,当有Animal的子类Dog、Cat的集合时,是否可以调用该方法,如果不能调用那么该方法该如何修改?
首先定义Animal及其子类Dog、Cat。
//Animal类
public class Animal {
public void s(){
System.out.println("动物叫");
}
}
//Dog类
public class Dog extends Animal{
public void s()
{
System.out.println("汪汪汪");
}
}
//Cat类
public class Cat extends Animal{
public void s() {
System.out.println("喵喵喵");
}
}
测试代码:
import java.util.ArrayList;
import java.util.List;
/*
* 泛型通配符
*/
public class AnimalListTest {
public static void main(String[] args) {
// 创建集合猫
List<Cat> list1 = new ArrayList<>();
list1.add(new Cat());
list1.add(new Cat());
list1.add(new Cat());
// 创建集合狗
List<Dog> list2 = new ArrayList<>();
list2.add(new Dog());
list2.add(new Dog());
list2.add(new Dog());
// 创建集合动物
List<Animal> list3 = new ArrayList<>();
list3.add(new Animal());
list3.add(new Animal());
list3.add(new Animal());
System.out.println("泛型上限:");
printList(list1);
System.out.println("--------------------------------");
printList(list2);
System.out.println("--------------------------------");
printList(list3);
System.out.println("--------------------------------");
System.out.println("泛型下限:");
printList1(list1);
System.out.println("--------------------------------");
printList1(list3);
}
// 泛型上限通配符 ? extends Animal
// 泛型通配符上限,表示只能是Animal或者Animal的子类
public static void printList(List<? extends Animal> list){
//遍历集合
for (int i = 0; i < list.size(); i++) {
Animal animal = list.get(i);
animal.s();
}
}
// 泛型下限通配符 ? super Cat
// 泛型通配符下限,表示只能是Cat或者Cat的父类
public static void printList1(List<? super Cat> list){
//遍历集合
for (int i = 0; i < list.size(); i++) {
Animal animal = (Animal)list.get(i);
animal.s();
}
}
}
输出结果:
泛型上限:
喵喵喵
喵喵喵
喵喵喵
--------------------------------
汪汪汪
汪汪汪
汪汪汪
--------------------------------
动物叫
动物叫
动物叫
--------------------------------
泛型下限:
喵喵喵
喵喵喵
喵喵喵
--------------------------------
动物叫
动物叫
动物叫
Process finished with exit code 0
包装类:
因为泛型不支持基本数据类型,只能支持对象类型(引用数据类型)。
为什么不支持基本数据类型?
泛型是jdk1.5推出的新特性,在JVM运行前会进行泛型擦除,将所有泛型都替换为Object
什么是包装类?
包装类就是把基本类型的数据包装成对象的类型,这个过程一般是自动的。
基本数据类型对应的包装类分别是:
基本数据类型 对应的包装类(引用数据类型)
byte Byte
short Short
int Integer
long Long
char Character
float Float
double Double
boolean Boolean
自动装箱:基本数据类型可以自动转换为包装类型。
自动拆箱:包装类型可以自动转换为基本数据类型。
包装类具备的其他功能:
可以把基本类型的数据转换成字符串类型。
public static String toString(double d)
public String toString()
可以把字符串类型的数值转换成数值本身对应的真实数据类型。
public static int parseInt(String s)
public static Integer valueOf(String s)
案例
自动装箱范围在 -128~127 之间
在 : 不会创建新的对象, 而是从底层数组中直接获取
不在 : 重新 new 出新的 Integer对象
public class IntegerDemo {
public static void main(String[] args) {
// -128~127缓存范围内的数字,使用同一个对象
Integer i1 = 127;
Integer i2 = 127;
// -128~127缓存范围外,创建新的对象
Integer i3 = 128;
Integer i4 = 128;
//比较的是地址
System.out.println(i1 == i2);
System.out.println(i3 == i4);
}
}
-----------------------
true
false
#学无止尽 #记录并分享我的学习日常