05.Java泛型类详细讲解

目录

一、泛型的定义

1.1、泛型保证数据的安全

1.2、消除强制类型转换

二、深入了解泛型

2.1、泛型类的使用

2.2、泛型函数

2.3、泛型接口

方式一:非泛型类实现泛型接口

方式二:泛型类实现泛型接口

2.4、多泛型变量

2.5、泛型类的边界约束

T extends Type 上界约束

T super anyClass 下界约束

多重边界约束

2.6、泛型实例的通配符

无界通配符?

上界通配符

下界通配符

2.7、Class 传递类的Class对象

三、泛型实际应用场景(对应业务场景)

3.1、Http请求返回结果封装

3.2、通用JSON格式化封装方法

3.3、无法实例化泛型类型的对象

四、附外参考资料


一、泛型的定义

  • 泛型的实质是将数据的类型参数化,也就是说类中操作的数据类型可以指定为一个参数(在定义类,接口,方法时为其增加数据类型的参数)
  • 泛型技术的核心目标是提高数据的安全性,减少强制类型转换,增强代码的可复用性(接下来会通过案例讲解)
  • 泛型的声明使用尖括号加一个字母,,其中的大写字母可以是任何字母,表示要传入的参数类型

定义语法

// 泛型类的定义
[修饰符] class 类名<T> // T必须是继承Object类的子类,如String,Integer,Double...
// 泛型接口的定义
[public] interface 接口名<T> 
// 泛型方法的定义
[public] [static] <T> 返回值类型 方法名(T参数) 

代码示范

// 泛型类
// T必须是继承Object父类的子类,如String,Integer,Double...
public class Test<T>{
    //该属性字段的使用T类型填充(数据类型未知)
    //使用泛型表示,运行时确定该字段的数据类型
    private T data;
}

// 泛型接口
public interface ITest<T>{}

// 静态泛型方法
public static <T> void getName(T a) {
    System.out.println(a.toString());
}

// 实例泛型方法
public <T> void getId(T a) {
    System.out.println(a.toString());
}

// 泛型类对象实例化
public static void main(String[] args) {
    // 泛型类在创建实例时,通过尖括号,传入一个String类型,类的内部会根据传入的这个数据类型做相应处理
    Test<String> obj = new Test<String>();
    // 也可以不对尖括号<>传参数的数据类型
    Test obj2 = new Test();
}
1.1、泛型保证数据的安全

Java中常用的List集合就是一种泛型类

List<String> list = new ArrayList<>();
list.add("Hello"); // 编译通过
list.add(123);     // 编译错误:不能添加 Integer 到 String 列表(编译时会检查数据类型是否匹配)
1.2、消除强制类型转换
// 1.如果不使用<>泛型,那么在获取元素时就要强制类型转换,这可能引发ClassCastException异常
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0); // 需要强制转换

// 2.使用<>泛型后,在获取元素时,无需转换数据类型(在类的内部就做好了数据类型的匹配处理)
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0); // 自动推断类型,无需转换

二、深入了解泛型

2.1、泛型类的使用
  • 注意代码案例中的T,T的传参不能是基本数据类型,必须是引用数据类型,如Integer,Double,String...
// 创建一个Box盒模型泛型类
public class Box<T> {
    // 设置数据类型为泛型
    private T value;

    // 设置泛型传参
    public void setValue(T value) {
        this.value = value;
    }
    // 返回泛型参数
    public T getValue() {
        return value;
    }
}

// Test.java
public class Test {
    
    public static void main(String[] args) {
        
        // 普通类的构造
        Box box = new Box();
        
        // 泛型类的构造
        // 创建泛型类对象实例, <Integer> 表示传入Integer类型
        // 类内部相相关的属性参数会自动推导为Integer
        Box<Integer> intBox = new Box<>();
        intBox.setValue(100);
        int num = intBox.getValue();
        
        Box<String> strBox = new Box<>();
        strBox.setValue("Hello");
        String text = strBox.getValue();
    }
}
2.2、泛型函数
public class FanMethods {  
    

    //静态泛型函数  
    public static  <T> void staticMethod(T a){  
        System.out.println("静态泛型函数"+a.toString());
    }  
    //实例泛型函数  
    public  <T> void OtherMethod(T a){  
        System.out.println("对象实例泛型函数"+a.toString());
    }
}  
// Test.java
public class Test{
    public static void main(String[] args) {
        // 静态泛型方法调用
        FanMethods.staticMethod("adfdsa");//使用方法一,此种方式根据传参参数类型隐式传递
        FanMethods.<String>staticMethod("adfdsa");//使用方法二,这种编写方式是为了代码更加清晰,表明调用了泛型函数
          
        // 实例泛型函数调用
        // 需要先创建实例对象才能调用
        FanMethods fanMethod = new FanMethods();  
        fanMethod.OtherMethod(new Integer(123));//使用方法一  
        fanMethod.<Integer>OtherMethod(new Integer(123));//使用方法二 
        
    }
}
2.3、泛型接口
方式一:非泛型类实现泛型接口
// 在接口上定义泛型   
interface Info<T>{         
    // 定义抽象方法,抽象方法的返回值就是泛型类型   
    T getVar() ;  
    void setVar(T x);  
}

// 定义泛型接口的实现类
// 注意这段代码Info<String>,在实现接口方法时,会自动将方法的泛类型填充为String
class InfoImpl implements Info<String>{
    // 定义属性
    private String var ;                
    // 通过构造方法设置属性内容  
    public InfoImpl(String var){        
        this.setVar(var) ;  
    }
    
    // 重写接口方法
    // 因为显式定义了Info<String>,所以这里会自动设置String数据类型
    @Override
    public void setVar(String var){  
        this.var = var ;  
    }  
    @Override  
    // 因为显式定义了Info<String>,所以这里会自动设置String数据类型
    public String getVar(){  
        return this.var ;  
    }  
}  
  
public class GenericsDemo24{  
    public void main(String arsg[]){  
        InfoImpl i = new InfoImpl("harvic");  
        System.out.println(i.getVar()) ;  
    }  
};
方式二:泛型类实现泛型接口

使用泛型类来实现泛型接口就是为了让用户定义接口所需要的数据类型,而非方式一那样直接写死

interface Info<T>{
    public T getVar() ; 
    public void setVar(T var);
}
// 定义泛型接口实现类
// 在构造实现类时, 定义InfoImpl<T>泛型类来实现泛型接口
class InfoImpl<T> implements Info<T>{
    // 定义属性泛型
    private T var;
    // 通过构造方法设置属性内容
    public InfoImpl(T var){     
        this.setVar(var) ;    
    }  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
}  
public class GenericsDemo24{  
    public static void main(String arsg[]){  
        InfoImpl<String> i = new InfoImpl<String>("harvic");  
        InfoImpl<Integer> i2 = new InfoImpl<Integer>(222);  
        System.out.println(i.getVar()) ;  
        System.out.println(i2.getVar()) ;  
    }  
};  
2.4、多泛型变量

可以一次定义多个泛型变量

通常来说,类型参数不限于任何字母,但其一般都有对应语义

  •  E — Element,常用在java Collection里,如:List,Iterator,Set
  •  K,V — Key,Value,类似Map的键值对key-Value
  •  N — Number,数字
  •  T — Type,类型,如String,Integer等等
class InfoImpl<T,K,U> implements Info<U>{   // 定义泛型接口的子类  
     private U var ;      
     private T x;  
     private K y;  
     public InfoImpl(U var){        // 通过构造方法设置属性内容  
         this.setVar(var) ;  
     }  
     public void setVar(U var){  
         this.var = var ;  
     }  
     public U getVar(){  
         return this.var ;  
     }  
 }  
2.5、泛型类的边界约束

在定义泛型类时,Java默认可以使用任何数据类型来实例化泛型对象,但Java语言也可以在使用泛型类创建对象时,对传递的类型参数做出类型限制。

T extends Type 上界约束
// class className <T extends anyClass>
// anyClass可以是一个类,也可以是一个接口。
// 在实例化泛型类对象时,T传参类型的类只能是anyClass类或继承了anyClass类的子类。
// 如果anyClass是接口,则传入的类型参数只能是实现了anyClass这个接口的类

// 代码案例
public class Box<T extends Number> {
    private T value;
    // 只能存储 Number 或其子类(如 Integer、Double)
}
T super anyClass 下界约束
// class className <T super anyClass>
// 必须是anyClass类或其父类,如果anyClass是接口,则必须是anyClass接口或者它的父接口

// 代码案例
public class Box<T super Integer> {
    private T value; //元素类型只能是Integer或其父类(如Number、Object等)。
    
}
多重边界约束
public class Example<T extends Number & Comparable<T>> {
    // T 必须是 Number 的子类且实现了 Comparable 接口
}
2.6、泛型实例的通配符
无界通配符?

? 表示任意类型,适用于只读操作

List<?> list = new ArrayList<String>();
String s = list.get(0); // 允许读取,但类型为 Object
list.add("Hello");      // 编译错误:禁止写入
上界通配符<? extends T>

表示类型必须是 T 或其子类,适用于只读场景

List<? extends Number> numbers = new ArrayList<Integer>();
Number n = numbers.get(0); // 允许读取 Number 类型
numbers.add(10);           // 编译错误:禁止写入
下界通配符<? super T>

表示类型必须是 T 或其父类,适用于写入场景

List<? super Integer> list = new ArrayList<Number>();
list.add(10); // 允许写入 Integer
Integer i = (Integer) list.get(0); // 需要强制转换
2.7、Class 传递类的Class对象

先简单介绍下Class对象

  • Class对象:每个类都有一个Class对象,Class对象是反射机制的核心,是Java运行时为每个类或者接口生成的一个特殊对象。
    • JVM在加载类时都会在内存中为其创建对应的Class对象(单例对象)
    • Class对象包含了一个类的所有结构信息
      • 类名
      • 父类
      • 实现的接口
      • 构造方法
      • 成员变量
      • 方法
      • 注解等元数据

现在回到泛型对Class对象的应用:在实际业务场景中,我们经常会遇到解析JSON字符串的需求,代码如下

public static List<SuccessModel> parseArray(String response){ 
    // JSON.parseArray(String jsonStr,Class classObj) 属于FastJSON框架的API,参数1传递json字符串,参数2传递一个class对象
    List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);  
    return modelList;  
}


public static List<SuccessModelB> parseArray2(String response){ 
    // JSON.parseArray(String jsonStr,Class classObj) 属于FastJSON框架的API,参数1传递json字符串,参数2传递一个class对象
    List<SuccessModelB> modelList = JSON.parseArray(response, SuccessModelB.class);  
    return modelList;  
}

上面代码在书写格式上没有任何问题,但假如现在有一个新的类型SuccessModelB需要做解析,难道再编写一个代码几乎一致的函数吗?

这时候可以通过泛型封装成一个公共函数,解决代码复用问题

public class JsonUtils{
    
    // Class<T> object传递类的class对象
    public static <T> List<T> parseArray(String response,Class<T> object){  
        List<T> resultList = JSON.parseArray(response, object);  
        return resultList;  
    }

}
 
// 代码使用案例
public class Test{
    public static void main(String[] args) {
        String jsonStr = "[{name: 'xxx', age: 20}, {name: 'yyy', age: 22}, {name: 'zzz', age: 30}]"
        List<Person> person = JsonUtils.parseArray(jsonStr,Person.class);
        List<PersonB> person = JsonUtils.parseArray(jsonStr,PersonB.class);
    }
}

三、泛型实际应用场景(对应业务场景)

3.1、Http请求返回结果封装

ResultVo.java

import lombok.Getter;

@Getter
public class ResultVO<T> {
    /**
     * 状态码,比如1000代表响应成功
     */
    private int code;
    /**
     * 响应信息,用来说明响应情况
     */
    private String msg;
    /**
     * 响应的具体数据
     */
    private T data;

    public ResultVO(T data) {
        this(ResultCode.SUCCESS, data);
    }

    public ResultVO(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = data;
    }
}

RpcServiceResult.java

import lombok.Data;
/**
 * RPC服务返回结果
 */
@Data
public class RpcServiceResult<T> {
    private boolean success;
    private String errorCode;
    private String errorMsg;
    private T data;

    public static <T> RpcServiceResult<T> getSuccessResult(T t) {
        RpcServiceResult<T> result = new  <>();
        result.setSuccess(true);
        result.setData(t);
        return result;
    }
    public static <T> RpcServiceResult<T> getFailureResult(
    String code, String msg) {
        RpcServiceResult<T> result = new RpcServiceResult<>();
        result.setSuccess(false);
        result.setErrorCode(code);
        result.setErrorMsg(msg);
        return result;
    }
}
3.2、通用JSON格式化封装方法
/**
 * 将数组JSON字符串格式化返回List结果集
 * @param response 响应的结果数据集,JSON字符串
 * @param clas 要格式化的目标类Class对象
 */
public static <T> List<T> parseArray(String response,Class<T> clas){  
    List<T> modelList = JSON.parseArray(response, clas);  
    return modelList;  
}

/**
 * 将对象JSON字符串格式化返回对象实例结果,如果格式化失败则返回null
 * @param json 响应的结果数据集,JSON字符串
 * @param clas 要格式化的目标类Class对象
 */
public static <T> T parseJsonToObj(String json, Class<T> clas) {
    try {
        JSONObject jsonObject = JSONObject.parseObject(json);
        return JSON.toJavaObject(jsonObject, clas);
    } catch (Exception e) {
        LOG.error(e.getMessage());
    }
    return null;
}
3.3、无法实例化泛型类型的对象
public class Box<T> {
    private T value;

    public Box(Class<T> clazz) throws Exception {
        // 通过反射机制创建一个对象
        // .getDeclaredConstructor()获取无参构造
        // .newInstance()创建泛型的实例对象
        this.value = clazz.getDeclaredConstructor().newInstance();
    }
}

四、附外参考资料

Class对象详解

47. Class 对象详解_class撖寡情-优快云博客

<think> 首先,用户要求用中文回答问题。问题主题是“Java讲解”,所以我需要详细讲解Java。 关键点: - Java允许在编译时提供类安全。 - 主要概念:类参数、泛型类接口、方法。 - 优点:避免类转换错误、提高代码重用性。 - 例子:如ArrayList<String>、HashMap<K,V>。 - 注意事项:类擦除、通配符(如<?>, <? extends T>, <? super T>)。 结构我的回答: 1. 简要介绍。 2. 解释核心概念。 3. 给出简单代码示例。 4. 讨论优点和限制。 5. 结束时必须添加3-5个相关问题。 结尾要求: - 第一行必须是“§§相关问题§§”。 - 然后列出3-5个相关问题。 - 禁止换行分散;问题应该在一行中列出,用数字和点分隔。 例如: §§相关问题§§ 1. ··· 2. ··· 3. ··· 我需要确保回答完整后,在末尾添加这个部分。 脑补回答内容: - 介绍:Java是JDK 5.0引入的特性,用于在类、接口和方法中使用类参数。 - 核心:类参数如<E>,在编译时指定类。 - 示例代码:定义一个简单的泛型类。 - 优点:类安全、减少运行时错误。 - 限制:类擦除意味着运行时类信息丢失。 相关问题: 1.擦除是什么?它是如何工作的? 2. 通配符在中的作用是什么? 3. 方法如何定义和使用? 4. Java与C++模板有什么不同? 5. 在集合框架中的应用例子? 确保有3-5个问题。 完整回答格式: - 先写讲解。 - 然后代码示例(如果需要)。 - 最后加“§§相关问题§§”和问题列表。 代码示例:因为用户提供了Python示例,我应该提供Java代码示例。 例如: ```java public class Box<T> { private T t; public void set(T t) { this.t = t; } public T get() { return t; } } ``` 现在,组装回答。 </think> JavaJava编程语言中的一个重要特性,自JDK 5.0引入,用于在编译时提供类安全。它允许在类、接口或方法中定义类参数,从而避免运行时类转换错误,并提高代码的可重用性和可读性。以下是核心概念的讲解: 1. **泛型类**:通过类参数定义类,例如`Box<T>`,其中`T`是占位符类。 2. **接口**:类似类,但用于接口,如`List<E>`。 3. **方法**:在方法中独立使用类参数,例如`public <T> void printElement(T element)`。 4. **优点**: - **类安全**:编译时检查类,防止`ClassCastException`。 - **代码重用**:一个泛型类可以处理多种类。 - **减少强制转换**:编译器自动处理类。 5. **限制与注意事项**: - **类擦除**:在编译后,泛型类信息被擦除(如`List<String>`在运行时变为`List<Object>`)。 - **通配符**:用于处理未知类,如`<?>`, `<? extends Number>`, `<? super Integer>`。 - **不能用于基本类**:泛型类必须是对象类(如`Integer`而不是`int`)。 示例代码:定义一个简单的泛型类并使用它。 ```java // 泛型类定义 public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } public static void main(String[] args) { Box<String> stringBox = new Box<>(); // 使用 stringBox.setContent("Hello, Generic!"); System.out.println(stringBox.getContent()); // 输出: Hello, Generic! } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星夜温蓝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值