在 Java 中,方法前面加上 <T> 表示该方法是一个泛型方法。泛型方法允许你在方法签名中指定一个或多个类型参数,从而使得该方法可以处理多种类型的对象。这增加了代码的灵活性和复用性。
一、基本语法
<T1, T2, ..., Tn> 返回类型 方法名(形参列表) {
// 方法体
}
- <T1, T2, ..., Tn>:类型参数列表,可以有一个或多个类型参数。
- 返回类型:方法的返回类型,可以是具体的类型,也可以是类型参数。
- 方法名:方法的名称。
- 形参列表:方法的参数列表,可以使用类型参数作为参数类型。
二、代码示例
1. 单个类型参数的泛型方法
假设现在富士康(Foxconn)要生产一批手机,它给手机厂商定下了一个规范:
package com.study.genericity.entity;
/**
* 富士康的手机生产规范
*/
public abstract class MobilePhone {
abstract void produce();
}
OPPO、Huawei 都按照 富士康 的规范(extends MobilePhone)生产手机:
package com.study.genericity.entity;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class OPPO extends MobilePhone {
/** 型号 */
private String model;
/** 颜色 */
private String color;
/** 运行内存 */
private String ram;
/** 存储 */
private String rom;
@Override
public void produce() {
System.out.println("正在生产 OPPO手机 :型号-"+model+" 颜色-"+color+" 运行内存-"+ram+" 存储容量-"+rom);
}
}
----------------------------------------
package com.study.genericity.entity;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Huawei extends MobilePhone {
/** 型号 */
private String model;
/** 颜色 */
private String color;
/** 是否折叠 */
private boolean foldOrNot;
/** 运行内存 */
private String ram;
/** 存储 */
private String rom;
@Override
public void produce() {
String description = "正在生产 华为手机 :"+(foldOrNot?"折叠屏 ":"")+model+" "+color+" "+ram+" "+rom;
System.out.println(description);
}
}
富士康 为他们开了一条通用生产线生产手机:
package com.study.genericity.entity;
public class Foxconn {
/** 富士康通用生产线 */
public static void produce(MobilePhone mobilePhone) {
mobilePhone.produce();
}
public static void main(String[] args) {
OPPO oppo = new OPPO("Find X7", "白日梦想家", "12GB", "256GB");
produce(oppo);
Huawei huawei = new Huawei("Pocket 2", "洛可可白", true, "12GB", "256GB");
produce(huawei);
}
}
输出:
正在生产 OPPO手机 :型号-Find X7 颜色-白日梦想家 运行内存-12GB 存储容量-256GB
正在生产 华为手机 :折叠屏 Pocket 2 洛可可白 12GB 256GB
这个时候来了苹果,他们委托 富士康 生产新版 iPhone。
不过苹果 它财大气粗,它有自己的手机生产规范:
package com.study.genericity.entity;
/**
* 苹果自己的移动产品生产规范
*/
public abstract class AppleMobile {
abstract void producePhone();
}
----------------------------------------
package com.study.genericity.entity;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Apple extends AppleMobile {
/** 型号 */
private String model;
/** 颜色 */
private String color;
/** 运行内存 */
private String ram;
/** 存储 */
private String rom;
@Override
public void producePhone() {
System.out.println("正在生产 尊贵的iPhone :"+model+" "+color+" "+ram+" "+rom);
}
}
富士康 本来想拒绝,因为苹果的生产流程已经继承了 AppleMobile 规范, 无法再继承 MobilePhone 规范,两个规范并不兼容(Java 单继承)。但如果为苹果单开一条生产线,成本又特别高昂。
package com.study.genericity.entity;
public class Foxconn {
/** 富士康通用生产线 */
public static void produce(MobilePhone mobilePhone) {
mobilePhone.produce();
}
/** 为苹果单开一条流水线 */
public static void produce(AppleMobile appleMobile) {
appleMobile.producePhone();
}
public static void main(String[] args) {
OPPO oppo = new OPPO("Find X7", "白日梦想家", "12GB", "256GB");
produce(oppo);
Huawei huawei = new Huawei("Pocket 2", "洛可可白", true, "12GB", "256GB");
produce(huawei);
Apple apple = new Apple("iPhone 16 Pro", "白", "16GB", "128GB");
produce(apple);
}
}
输出:
正在生产 OPPO手机 :型号-Find X7 颜色-白日梦想家 运行内存-12GB 存储容量-256GB
正在生产 华为手机 :折叠屏 Pocket 2 洛可可白 12GB 256GB
正在生产 尊贵的iPhone :iPhone 16 Pro 白 16GB 128GB
这可愁坏了 富士康 的老总,这个时候,聪明的工程师 小林 为老板想了一个办法,他用 泛型方法 的方式,定义了一条通用的流水线,可以忽略类型规范:
package com.study.genericity.entity;
import java.util.function.Consumer;
public class Foxconn {
/** 用 泛型方法 定义通用生产流水线 */
public static <T> void produce(Consumer<T> consumer, T phone) {
consumer.accept(phone);
}
public static void main(String[] args) {
OPPO oppo = new OPPO("Find X7", "白日梦想家", "12GB", "256GB");
produce(OPPO::produce, oppo);
Huawei huawei = new Huawei("Pocket 2", "洛可可白", true, "12GB", "256GB");
produce(Huawei::produce, huawei);
Apple apple = new Apple("iPhone 16 Pro", "白", "16GB", "128GB");
// 苹果 定制化生产
produce(Apple::producePhone, apple);
}
}
输出:
正在生产 OPPO手机 :型号-Find X7 颜色-白日梦想家 运行内存-12GB 存储容量-256GB
正在生产 华为手机 :折叠屏 Pocket 2 洛可可白 12GB 256GB
正在生产 尊贵的iPhone :iPhone 16 Pro 白 16GB 128GB
富士康 老板高兴坏了,因为这条流水线太好用,老板甚至放弃了自定义的手机生产规范 MobilePhone,给手机厂商更高的自由度(不用再继承 MobilePhone),去生产定制化的手机产品:
package com.study.genericity.entity;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class Huawei {
/** 型号 */
private String model;
/** 颜色 */
private String color;
/** 是否折叠 */
private boolean foldOrNot;
/** 运行内存 */
private String ram;
/** 存储 */
private String rom;
/** 定制化生产 */
public void produceHuawei() {
String description = "正在生产 华为手机 :"+(foldOrNot?"折叠屏 ":"")+model+" "+color+" "+ram+" "+rom;
System.out.println(description);
}
}
---------------------------------------------
package com.study.genericity.entity;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class OPPO {
/** 型号 */
private String model;
/** 颜色 */
private String color;
/** 运行内存 */
private String ram;
/** 存储 */
private String rom;
/** 定制化生产 */
public void produceOPPO() {
System.out.println("正在生产 OPPO手机 :型号-"+model+" 颜色-"+color+" 运行内存-"+ram+" 存储容量-"+rom);
}
}
---------------------------------------------
package com.study.genericity.entity;
import java.util.function.Consumer;
public class Foxconn {
/** 用 泛型方法 定义通用生产流水线 */
public static <T> void produce(Consumer<T> consumer, T phone) {
consumer.accept(phone);
}
public static void main(String[] args) {
OPPO oppo = new OPPO("Find X7", "白日梦想家", "12GB", "256GB");
// OPPO 定制化生产
produce(OPPO::produceOPPO, oppo);
Huawei huawei = new Huawei("Pocket 2", "洛可可白", true, "12GB", "256GB");
// 华为 定制化生产
produce(Huawei::produceHuawei, huawei);
Apple apple = new Apple("iPhone 16 Pro", "白", "16GB", "128GB");
// 苹果 定制化生产
produce(Apple::producePhone, apple);
}
}
输出:
正在生产 OPPO手机 :型号-Find X7 颜色-白日梦想家 运行内存-12GB 存储容量-256GB
正在生产 华为手机 :折叠屏 Pocket 2 洛可可白 12GB 256GB
正在生产 尊贵的iPhone :iPhone 16 Pro 白 16GB 128GB
2、返回类型也是泛型的泛型方法
输入:
一个列表,和 几个数据。
输出:
数据包含在 列表 中的一个新的列表。
示例:
输入 [2,5,7,9,10] 和 5,27 输出 [5]
输入 ["福建", "江西", "浙江"] 和 "江苏","福建" 输出 ["福建"]
一种 泛型方法 的实现:
package com.study.genericity.entity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
@SafeVarargs
public static <T> List<T> filter(List<T> list, T... args) {
List<T> result = new ArrayList<T>();
for (T t : args) {
if (list.contains(t)) {
result.add(t);
}
}
return result;
}
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(2,5,7,9,10);
System.out.println( filter(list1, 5, 27) );
List<String> list2 = Arrays.asList("福建", "江西", "浙江");
System.out.println( filter(list2, "江苏", "福建") );
}
}
输出:
[5]
[福建]
在这个例子中,filter 方法是一个泛型方法,它的入参是类型参数<T>的 泛型列表 List<T> 和 类型参数<T>的可变参数;它的返回类型还是 类型参数<T>的 泛型列表 List<T>。
这种返回类型也是泛型的泛型方法,在很多框架中都有大量的使用。
比如 cn.hutool.json.JSONUtil :
// 根据 JSON 字符串和 bean Class 类型,解析 JSON 数据 为 指定类型
public static <T> T toBean(String jsonString, Class<T> beanClass) {
return toBean(parseObj(jsonString), beanClass);
}