Java中泛型的使用


Java中泛型的使用,及具体场景

在Java中,有两种使用泛型的方式:

  1. 类/接口上声明泛型参数,那么类中所有方法都可以使用这些类型参数。
  2. 方法上声明泛型参数,这样这个方法就可以独立于类使用自己的泛型。

一、在类中定义泛型

代码示例:

抽象类

import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

@Slf4j
public abstract class EventHandle<T> {
    private final Class<T> clazz;

    protected EventHandle(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void excecute(String eventData) {
        T model = JSON.parseObject(eventData, clazz);
        if (Objects.isNull(model)) {
            log.info("eventData is null");
            return;
        }
        handle(model);
    }

    protected abstract void handle(T obj);
}

实现类

@Slf4j
public class EquipOtaProgressEventHandle extends EventHandle<DeviceOtaProgressModel> {
    @Autowired
    private RedisCacheService redisCacheService;

    @Autowired
    private MessageService messageService;

    public EquipOtaProgressEventHandle() {
        super(DeviceOtaProgressModel.class);
    }

    /**
     * 
     *
     * @param model
     */
    @Override
    protected void handle(DeviceOtaProgressModel model) {
        log.info("deviceOtaProgressResultModel={}", JSON.toJSONString(model));
        //数据构造
        EquipmentOtaProgressVO vo = buildVo(model);
        // 业务逻辑
    }

}

二、在方法中定义泛型

场景,在一个类中给某一个方法定义了泛型的使用。这个泛型定义只针对该方法,也只作用于这个方法。

public class KafkaMsgSender {
    
    public void postDeviceOnline(String name, DeviceEnum DeviceEnum) {
       // 业务逻辑。。。
    }

    public <T> void postCoreEvent(String name, T params, String method) {
        BaseRequestModel<T> baseRequestModel = new BaseRequestModel<>(params, method);
        String message = JSON.toJSONString(baseRequestModel);   
    }
    
}

为什么要在 void前面加 <T>

一、核心原因

<T>是泛型方法的「类型参数声明」,它告诉编译器:这是一个泛型方法,T是一个类型占位符,而不是一个具体的类名。


二、代码解析

public <T> void postCoreEvent(String name, T params, String method) {
    BaseRequestModel<T> baseRequestModel = new BaseRequestModel<>(params, method);
    String message = JSON.toJSONString(baseRequestModel);   
}

各部分含义

部分作用
<T>声明类型参数(必须)
void方法返回值
T params使用类型参数 T

三、为什么不能省略 <T>

1. 错误写法(编译失败)

// ❌ 编译错误:找不到符号 T
public void postCoreEvent(String name, T params, String method) {
    // 编译器会认为 T 是一个具体的类
    // 如果项目中没有 T.java,直接报错
}

原因

没有 <T>,编译器会把 T当作一个具体的类名,而不是泛型。


2. 正确写法(两种)

写法一:泛型方法(推荐)

public <T> void postCoreEvent(String name, T params, String method) {
    // T 是泛型参数
}

写法二:类级别泛型

public class EventPublisher<T> {
    
    // 不需要在方法上再写 <T>
    public void postCoreEvent(String name, T params, String method) {
        // T 来自类定义
    }
}

四、对比示例

1. 泛型方法(独立泛型)

public class EventUtil {
    
    // 方法级别的泛型:每次调用可以传入不同类型
    public <T> void postCoreEvent(String name, T params, String method) {
        System.out.println(params.getClass()); // 运行时类型
    }
}

// 调用
eventUtil.postCoreEvent("event1", new User(), "create");  // T = User
eventUtil.postCoreEvent("event2", "hello", "notify");     // T = String

2. 类级别泛型(固定类型)

public class EventPublisher<T> {
    
    // 类级别的泛型:整个类使用同一种类型
    public void postCoreEvent(String name, T params, String method) {
        System.out.println(params.getClass());
    }
}

// 使用
EventPublisher<User> publisher = new EventPublisher<>();
publisher.postCoreEvent("event1", new User(), "create");  // T 固定为 User
// publisher.postCoreEvent("event2", "hello", "notify"); // ❌ 编译错误:需要 User 类型

五、为什么代码必须加 <T>

BaseRequestModel<T> baseRequestModel = new BaseRequestModel<>(params, method);

这里:

  • BaseRequestModel<T>是一个泛型类
  • 需要传入一个类型参数 T
  • 这个 T必须来自方法声明 <T>

六、常见误解澄清

误解 1:参数里的 T已经声明了泛型

// 错误理解
public void postCoreEvent(String name, T params, String method)
// 这里的 T 不是声明,而是使用

真相

  • 声明泛型:<T>(在返回类型前)
  • 使用泛型:T params(在参数中)

误解 2:泛型只在参数里用,不需要声明

// 编译错误
public void postCoreEvent(String name, List<T> params) {
    // List<T> 中的 T 也需要先声明
}

正确写法

public <T> void postCoreEvent(String name, List<T> params) {
    // 先声明 <T>,才能使用 List<T>
}

七、完整示例

1. 泛型方法的正确使用

public class EventService {
    
    // 泛型方法
    public <T> void postCoreEvent(String name, T params, String method) {
        BaseRequestModel<T> request = new BaseRequestModel<>(params, method);
        System.out.println("Event: " + name);
        System.out.println("Method: " + method);
        System.out.println("Params: " + params);
    }
    
    // 多泛型参数
    public <T, R> R convert(T source, Function<T, R> converter) {
        return converter.apply(source);
    }
}

2. 调用示例

EventService service = new EventService();

// T = String
service.postCoreEvent("USER_LOGIN", "user123", "login");

// T = UserDTO
UserDTO user = new UserDTO("张三", 18);
service.postCoreEvent("USER_CREATE", user, "create");

// T = Integer, R = String
String result = service.convert(123, i -> "数字:" + i);

八、最佳实践建议

1. 方法独立泛型 → 用 <T>

public <T> void publish(T event) {
    // 方法级别的泛型
}

2. 类固定泛型 → 不用 <T>

public class Publisher<T> {
    public void publish(T event) {
        // 类级别的泛型
    }
}

3. 多个泛型参数

public <T, R> R handle(T request, Class<R> responseType) {
    // T 是请求类型,R 是响应类型
}

4. 有界泛型

public <T extends BaseEvent> void publish(T event) {
    // T 必须是 BaseEvent 的子类
}

九、总结

问题答案
为什么要加 <T>声明这是一个泛型方法,T是类型参数
参数里的 T算声明吗?不算,那是使用
不加 <T>会怎样?编译错误:找不到符号 T
什么时候可以不加?当类已经声明了泛型 <T>

一句话总结

<T>是泛型方法的“身份证”,没有它,编译器不知道 T是类型参数还是具体类。

JAVA-技能点
知识点