【Java泛型】带你玩转泛型那些事

本文详细介绍了Java中的泛型,包括泛型的定义、自定义泛型类和接口、泛型方法、泛型限定以及泛型擦除的基本概念和使用示例。通过实例展示了如何在编译阶段利用泛型避免错误,并指出基本类型与引用类型的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、认识泛型

所谓泛型指的是,在定义类、接口、方法时,同时声明了一个或者多个类型变量(如:),称为泛型类、泛型接口、泛型方法、它们统称为泛型。

比如我们前面学过的ArrayList类就是一个泛型类,我们可以查看ArrayList类的声底层实现如下所示:
在这里插入图片描述
ArrayList集合的设计者在定义ArrayList集合时,就已经明确ArrayList集合时给别人装数据用的,但是别人用ArrayList集合时候,装什么类型的数据他不知道,所以就用一个<E>表示元素的数据类型。

当别人使用ArrayList集合创建对象时,new ArrayList<String> 就表示元素为String类型,new ArrayList<Integer>表示元素为Integer类型。

泛型的作用、本质:

  • 泛型的好处:在编译阶段可以避免出现一些非法的数据。

  • 泛型的本质:把具体的数据类型传递给类型变量。

二、自定义泛型

接下来我们学习一下自定义泛型类,但是有一些话需要给大家提前交代一下:泛型类,在实际工作中一般都是源代码中写好,我们直接用的,就是ArrayList这样的,自己定义泛型类是非常少的。

自定义泛型类的格式如下

//这里的<T,W>其实指的就是类型变量,可以是一个,也可以是多个。字母随意
public class 类名<T,W>{
    
}

接下来,我们自己定义一个MyArrayList泛型类,模拟一下自定义泛型类的使用。注意这里重点仅仅只是模拟泛型类的使用,所以方法中的一些逻辑是次要的,也不会写得太严谨。

//定义一个泛型类,用来表示一个容器
//容器中存储的数据,它的类型用<E>先代替用着,等调用者来确认<E>的具体类型。
public class MyArrayList<E>{
    private Object[] array = new Object[10];
    //定一个索引,方便对数组进行操作
    private int index;
    
    //添加元素
    public void add(E e){
        array[index]=e;
        index++;
    }
    
    //获取元素
    public E get(int index){
        return (E)array[index];
    }
}

接下来,我们写一个测试类,来测试自定义的泛型类MyArrayList是否能够正常使用
public class Test{
public static void main(String[] args){
//1.确定MyArrayList集合中,元素类型为String类型
MyArrayList list = new MyArrayList<>();
//此时添加元素时,只能添加String类型
list.add(“张三”);
list.add(“李四”);

     //2.确定MyArrayList集合中,元素类型为Integer类型
    MyArrayList<Integer> list1 = new MyArrayList<>();
    //此时添加元素时,只能添加String类型
    list.add(100);
    list.add(200);
    
}

}

三、自定义泛型接口

泛型接口其实指的是在接口中把不确定的数据类型用<类型变量>表示。定义格式如下:

//这里的类型变量,一般是一个字母,比如<E>
public interface 接口名<类型变量>{
    
}

比如,我们现在要做一个系统要处理学生和老师的数据,需要提供2个功能,保存对象数据、根据名称查询数据,要求:这两个功能处理的数据既能是老师对象,也能是学生对象。

首先我们得有一个学生类和老师类

public class Teacher{

}
public class Student{
    
}

我们定义一个Data<T>泛型接口,T表示接口中要处理数据的类型。

public interface Data<T>{
    public void add(T t);
    
    public ArrayList<T> getByName(String name);
}

接下来,我们写一个处理Teacher对象的接口实现类

//此时确定Data<E>中的E为Teacher类型,
//接口中add和getByName方法上的T也都会变成Teacher类型
public class TeacherData implements Data<Teacher>{
   	public void add(Teacher t){
        
    }
    
    public ArrayList<Teacher> getByName(String name){
        
    }
}

接下来,我们写一个处理Student对象的接口实现类

//此时确定Data<E>中的E为Student类型,
//接口中add和getByName方法上的T也都会变成Student类型
public class StudentData implements Data<Student>{
   	public void add(Student t){
        
    }
    
    public ArrayList<Student> getByName(String name){
        
    }
}

在实际开发中,一般也都是框架底层源代码把泛型接口写好,我们实现泛型接口就可以了。

四、泛型方法

泛型方法的格式如下

public <泛型变量,泛型变量> 返回值类型 方法名(形参列表){
    
}

下图中在返回值类型和修饰符之间有定义的才是泛型方法。
在这里插入图片描述
一个可能的案例如下所示:

public class Test{
    public static void main(String[] args){
        //调用test方法,传递字符串数据,那么test方法的泛型就是String类型
        String rs = test("test");
    
        //调用test方法,传递Dog对象,那么test方法的泛型就是Dog类型
    	Dog d = test(new Dog()); 
    }
    
    //这是一个泛型方法<T>表示一个不确定的数据类型,由调用者确定
    public static <T> T test(T t){
        return t;
    }
}

五、泛型限定

。泛型限定的意思是对泛型的数据类型进行范围的限制。有如下的三种格式

  • <?> 表示任意类型
  • <? extends 数据类型> 表示指定类型或者指定类型的子类
  • <? super 数据类型> 表示指定类型或者指定类型的父类

下面我们演示一下,假设有Car作为父类,BENZ,BWM两个类作为Car的子类,代码如下

class Car{}
class Benz extends Car{}
class BWN extends Car{}

public class Test{
    public static void main(String[] args){
        //1.集合中的元素不管是什么类型,test1方法都能接收
        ArrayList<BWM> list1 = new ArrayList<>();
        ArrayList<Benz> list2 = new ArrayList<>();
        ArrayList<String> list3 = new ArrayList<>();
        test1(list1);
        test1(list2);
        test1(list3);
        
        //2.集合中的元素只能是Car或者Car的子类类型,才能被test2方法接收
        ArrayList<Car> list4 = new ArrayList<>();
        ArrayList<BWM> list5 = new ArrayList<>();
        test2(list4);
        test2(list5);
        
        //2.集合中的元素只能是Car或者Car的父类类型,才能被test3方法接收
        ArrayList<Car> list6 = new ArrayList<>();
        ArrayList<Object> list7 = new ArrayList<>();
        test3(list6);
        test3(list7);
    }
    
    public static void test1(ArrayList<?> list){
        
    }
    
    public static void test2(ArrayList<? extends Car> list){
        
    }
    
   	public static void test3(ArrayList<? super Car> list){
        
    }
}

六、泛型擦除

泛型擦除也就是说泛型只能编译阶段有效,一旦编译成字节码,字节码中是不包含泛型的。而且泛型只支持引用数据类型,不支持基本数据类型。

PS:基本类型的变量保存原始值,即它代表的值就是数值本身;
而引用类型的变量保存引用值,”引用值”指向内存空间的地址,代表了某个对象的引用,而不是对象本身,
对象本身存放在这个引用值所表示的地址的位置。
基本类型包括:byte,short,int,long,char,float,double,Boolean,returnAddress,
引用类型包括:类类型,接口类型和数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值