Java基础——集合进阶5

一、泛型

1.1 什么是泛型?

泛型(Generics)是 Java 5 引入的一种机制,允许在定义类、接口或方法时使用"类型参数"(Type Parameter),从而在编译期提供类型安全检查,并避免强制类型转换。

💡 简单理解:

  • 泛型 = "参数化类型"
  • 就像方法可以接收参数一样,类/方法也可以接收"类型"作为参数

1.2 为什么需要泛型?------ 解决三大痛点

问题 没有泛型 有泛型
1. 类型安全 运行时才报错(如 ClassCastException 编译期检查,提前发现问题
2. 强制类型转换 需要手动 (String) obj 转换 自动类型推断,无需强转
3. 代码复用性差 为不同类型写重复代码 一套代码适配多种类型

泛型 = 编译期的类型安全 + 更简洁的代码

Java中的泛型是伪泛型,集合的限定是字符串内,仅仅只是在编译的时候检查是否是字符串,当数据真正添加到集合中的时候,集合还是会把这些数据当成Object类型来处理。拿出来用的时候,底层会做一个强转成字符串类型。

Java 的泛型是"伪泛型"------只存在于编译期,运行时会被"擦除"为原始类型(Raw Type)

⚠️ 为什么设计成类型擦除?

  • 兼容 Java 5 之前的代码(向后兼容)
  • 避免 JVM 大改(不需要为每种泛型生成新类)

💡 正因如此,泛型不能用于基本类型 (如 List<int> ❌),必须用包装类(List<Integer> ✅)

1.3 泛型的核心语法

泛型类

自己编写的时候,只能用<E>代表不确定是什么泛型,只用到别人调用了你写的泛型类才能确定。

java 复制代码
package com.lkbhua.Test;

import java.util.Arrays;

// 带有泛型的类
public class MyArraysList<E> {
    Object[] obj = new Object[10];
    int size = 0;

    // E:不确定的类型,该类型在类名后面已经定义过了
    // e:形参的名字,变量名
    public boolean add  (E e){
        obj[size] = e;
        size++;
        return true;
    }


    public E get(int index){
        // 强转
        return (E) obj[index];
    }

    // 打印对象的时候,出现的不是地址值,而是属性值
    @Override
    public String toString() {
        // 把数组里面所有的元素都转换成字符串拼接进行返回
        return Arrays.toString(obj);
    }
}
java 复制代码
package com.lkbhua.Test;

public class MyArraysTest1 {
    public static void main(String[] args) {
        MyArraysList<String> list = new MyArraysList<>();

        list.add("aaa");
        list.add("bbb");
        list.add("ccc");

        System.out.println(list);
    }
}

泛型方法

java 复制代码
package com.lkbhua.Test;

import java.util.ArrayList;

public class ListUtil {
    private ListUtil(){}

    // 类中定义一个静态方法addAll,用来添加多个集合的元素

    // 参数一:集合
    // 参数二:要添加的元素
    //
    public static <E>void addAll(ArrayList<E> list ,E e1,E e2,E e3,E e4){
        list.add(e1);
        list.add(e2);
        list.add(e3);
        list.add(e4);

    }
}
java 复制代码
package com.lkbhua.Test;

import java.util.ArrayList;

public class GenericsDemo {
    public static void main(String[] args) {
        /*泛型方法的练习:
            定义一个工具类:ListUtil
            类中定义一个静态方法addAll,用来添加多个集合的元素。*/

        ArrayList<String> list1 = new ArrayList<>();

        ListUtil.addAll(list1,"aaa","bbb","ccc","ddd");
        System.out.println(list1);

        ArrayList<Integer> list2 = new ArrayList<>();
        ListUtil.addAll(list2,1,2,3,4);
        System.out.println(list2);
    }
}

泛型接口

java 复制代码
public interface Comparable<T> {
    int compareTo(T other);
}

public class Student implements Comparable<Student> {
    private int id;
    public int compareTo(Student other) {
        return Integer.compare(this.id, other.id);
    }
}

1.4 类型参数命名规范(约定俗成)

虽然你可以用任意标识符(如 <A>),但业界通用如下:

字母 含义 示例
T Type(类型) Box<T>
E Element(元素,常用于集合) List<E>
K Key(键) Map<K, V>
V Value(值) Map<K, V>
N Number(数字) Calculator<N extends Number>
S, U, V 第二、第三、第四个类型 Pair<S, T>

1.5 泛型的继承

通配符 ? 用于表示"未知类型",增强泛型的灵活性。

1️⃣ 无界通配符:<?>

java 复制代码
public void printList(List<?> list) {
    for (Object obj : list) {
        System.out.println(obj);
    }
}

// 可以传入任何 List
printList(new ArrayList<String>());
printList(new ArrayList<Integer>());

✅ 只能读取(当作 Object),不能添加元素 (除了 null

2️⃣ 上界通配符:<? extends T> ------ PECS 原则:Producer Extends

表示"T 或 T 的子类 ",用于读取数据(生产者)

❌ 不能添加元素(因为不知道具体是哪个子类)

✅ 可以安全读取为 Number

3️⃣ 下界通配符:<? super T> ------ PECS 原则:Consumer Super

表示"T 或 T 的父类 ",用于写入数据(消费者)

java 复制代码
package com.lkbhua.Test.泛型类test;

import java.util.ArrayList;

public class demo2 {
    public static void main(String[] args) {
        /* 需求:
              定义一个方法,形参是一个集合,但是集合中的数据类型是不确定的

        */
        // 创建集合的对象
        ArrayList<Ye> list = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();

        show(list);
        show(list2);
        show(list3);


    }

    // 不确定什么类型可以使用泛型
    // 但是有弊端:
    // 此时他虽然可以接受任意的数据类型,
    // 但是有的时候我们只希望传递部分的数据类型
    // 比如:Ye 、Fu 、Zi,不愿意Student或者其他类
    // 难道写Ye? 但是泛型不具备继承性,意味着你只能用Ye,传递Ye的集合类型才能使用该方法
    // 泛型的通配符:?
    //      ? 也表示不确定的类型
    //      它可以进行类型的限定
    //      ? extends E:表示可以传递E或者E所有的子类类型
    //      ? super E:  表示可以传递E或者E所有的父类类型

    // 表示可以传递Fu或者Fu所有的父类类型
    public static void show2(ArrayList<? super Fu> c) {
    }

    // 表示可以传递Ye或者Ye所有的子类类型
    public static void show1(ArrayList<? extends Ye> c) {
    }
    public static<E> void show(ArrayList<E> c) {

    }
}

// 应用场景:
// 1.集合中的数据类型不确定,但是集合中的数据类型是相同的
// 2.集合中的数据类型不确定,但是集合中的数据类型是继承关系

1.6 练习

继承关系比较简单,这里只给出测试代码:

java 复制代码
package com.lkbhua.Test.泛型类test.Text;

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {}

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public abstract void eat();
}
java 复制代码
package com.lkbhua.Test.泛型类test.Text;

import java.util.ArrayList;

public class test {
    public static void main(String[] args) {
        /* 测试类中定义一个方法用于饲养动物:
                public static void keepPet(ArrayList<???> list){
                    // 遍历集合,调用动物eat方法
                }
            需求1:该方法能养所有品种的猫,但是不能养狗
            需求2:该方法能养所有品种的狗,但是不能养猫
            需求3:该方法能养所有品种的猫和狗,但是不能传递其他的类型
        */

        ArrayList<LiHuaCat> list1 = new ArrayList<>();
        ArrayList<PersianCat> list2 = new ArrayList<>();
        ArrayList<HuskyDog> list3 = new ArrayList<>();
        ArrayList<Teddy> list4 = new ArrayList<>();

        // 需求测试1
        keepPet(list1);
        keepPet(list2);
        // keepPet(list3);
        // keepPet(list4);

        // 需求测试2
        keepPet1(list3);
        keepPet1(list4);
        // keepPet1(list1);
        // keepPet1(list2);

        // 需求测试3
        keepPet2(list1);
        keepPet2(list2);
        keepPet2(list3);
        keepPet2(list4);
        

    }

    public static void keepPet(ArrayList<? extends Cat> list) {

    }

    public static void keepPet1(ArrayList<? extends Dog> list) {

    }

    public static void keepPet2(ArrayList<? extends Animal> list) {

    }

}

声明:

分析借鉴于通义AI

以上均来源于B站@ITheima的教学内容!!!

本人跟着视频内容学习,整理知识引用

相关推荐
WZTTMoon2 小时前
Spring 配置解析与 @Value 注入核心流程详解
java·spring boot·spring
聪明努力的积极向上2 小时前
【C#】System.Text.Encoding.Default 属性在framework和.netcore中的区别
开发语言·c#·.netcore
程序定小飞3 小时前
基于springboot的健身房管理系统开发与设计
java·spring boot·后端
wxin_VXbishe3 小时前
springboot在线课堂教学辅助系统-计算机毕业设计源码07741
java·c++·spring boot·python·spring·django·php
夕泠爱吃糖3 小时前
template关键字
开发语言·c++·template
ceclar1233 小时前
C++文件操作
开发语言·c++
信仰_2739932433 小时前
RedisCluster客户端路由智能缓存
java·spring·缓存
兰雪簪轩3 小时前
仓颉语言内存布局优化技巧:从字节对齐到缓存友好的深度实践
java·spring·缓存
高一要励志成为佬3 小时前
【C++】vector的迭代器失效问题,(什么是迭代器失效,为什么会产生迭代器失效,怎么避免迭代器失效问题)
开发语言·c++