Java泛型基础
Preface
- Generic Types
- type parameter names are single, uppercase letters
- The most commonly used type parameter names are:
- E - Element (used extensively by the Java Collections Framework)
- K - Key
- N - Number
- T - Type
- V - Value
- S,U,V etc. - 2nd, 3rd, 4th types
1. 基本概念
- 本质:泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入
- 没有指定具体数据类型时,默认操作类型是 Object
- 只支持类类型,不支持基本类型
- 同一泛型类,本质上都是相同类型
- 好处
- 保证类型安全:泛型约束了变量的类型,保证了类型的安全性(在编译时就可检查类型匹配)
- 提高代码的重用性
- 消除强制类型转换:未使用泛型前,需要强制类型转换
2. 泛型基本用法
泛型类,是在实例化类的时候指明泛型的具体类型;
泛型方法,是在调用方法的时候指明泛型的具体类型,更加灵活
2.1 泛型类
泛型类:类名<T>,T 是类型占位符,表示一种引用类型
// T 表示类型参数
public class Result<T> {
private boolean success;
private String statusCode;
private String message;
// 方式一:作为变量类型
private T data;
// 方式二:作为方法参数
protected void success(T data) {
setData(data);
setSuccess(true);
}
// 方式三:作为方法返回值
public T getData() {
return data;
}
// Getter/Setter ...
}
class MultiResult<T, S, U> {
private T first;
private S second;
private U third;
}
使用示例:
public interface BusinessService {
Result<User> queryUser();
Result<Order> queryOrder();
}
2.2 泛型方法
- 泛型方法,就是在声明方法时定义一个或多个类型形参
- 当方法中的类型与类冲突时,方法中的 T 会覆盖类的 T
语法:[修饰符] [static] <T, R> 返回值类型 method(...) {}
注:<T> 必须存在,并且在方法返回值之前
public <T> T show(T t) {
return t;
}
Demo demo = new Demo();
String str = demo.show("str");
<T> 声明此方法为泛型方法,指定方法类型,此类型既可以在参数中用,也可以在返回值中用
public static <T> T test(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
return null;
}
}
public static <T> T test(Object obj) {
return (T) obj;
}
// 方法签名 - 多类型 <T, R>
public <T, R> R count(T t) {
}
public interface Service {
<T> ResultDTO<T> execute(ServiceCallback<T> callback, String serviceName);
}
public interface ServiceCallback<T> {
}
2.3 泛型接口
public interface QueryService<T, R> {
R execute(T obj);
}
泛型类派生子类,见 Java 泛型详解
public class QueryServiceImpl implements QueryService<String, Result> {
@Override
public Result execute(String obj) {
return null;
}
}
public class QueryServiceImpl2<T, R> implements QueryService<T, R> {
@Override
public R execute(T obj) {
return null;
}
}
QueryServiceImpl queryService = new QueryServiceImpl();
Result result = queryService.execute("111");
QueryServiceImpl2<String, Result> queryService2 = new QueryServiceImpl2<>();
Result result2 = queryService2.execute("222");
2.4 扩展:泛型接口和泛型方法混合
public interface Stream<T> extends BaseStream<T, Stream<T>> {
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
}
3. 泛型通配符
3.1 类型通配符(?)
- In generic code, the question mark (?), called the wildcard, represents an unknown type
-
List<?>
表示未知类型元素的 List,仅表示它是各种泛型 List 的父类,并不能把元素加入到其中
泛型类型不管继承关系,只管严格的匹配
例如:String 是 Object 的子类,但 List<String> 不是List<Object>类的子类
编译报错:不兼容的类型: java.util.List<java.lang.String>无法转换为java.util.List<java.lang.Object>
可将 List<Object>
改为 List<?>
,但还需进行强制类型转换
改进,使用受限制通配符List<? extends Shape>
,注意:因无法准确知道这个类型是什么,所以不能把 Shape 对象或其子类的对象加入这个泛型集合中
3.2 受限制通配符(泛型约束)
上界通配符 <? extends Type>
: Get First,协变,无法确定子类类型,只读
下界通配符 <? super Type>
: Put First,逆变,无法确定父类,只写
上界通配符
public static void main(String[] args) {
// public interface List<E> extends Collection<E> {}
List<String> strings = new ArrayList<>();
List<Object> objects = new ArrayList<>();
test(strings, objects); // 类型推断失败,编译报错
test2(strings, objects);
}
public static <T> void test(Collection<T> from, Collection<T> to) {
for (T ele : from) {
to.add(ele);
}
}
/**
* Collection<? extends T> 类型改为 T 的子类
*
* Collection<? extends T>: 类型通配符的表示方式
* Collection<T> to: 泛型方法的表示方式
*/
public static <T> void test2(Collection<? extends T> from, Collection<T> to) {
for (T ele : from) {
to.add(ele);
}
}
public static void main(String[] args) {
// public final class Integer extends Number implements Comparable<Integer> {}
List<Number> numbers = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
Number copy = copy(numbers, integers);
Integer integer = copy2(numbers, integers);
System.out.println(copy);
}
/**
* 返回类型只会与 dest 相同
*/
public static <T> T copy(Collection<T> dest, Collection<? extends T> src) {
T last = null;
for (T ele : src) {
last = ele;
dest.add(ele);
}
return last;
}
/**
* 改进:不管src集合元素的类型是什么,只要dest集合元素的类型与前者相同或是前者的父类即可
* @return
*/
public static <T> T copy2(Collection<? super T> dest, Collection<T> src) {
T last = null;
for (T ele : src) {
last = ele;
dest.add(ele);
}
return last;
}
// 思考:copy(List<? super T> dest, List<? extends T> src)
3.3 T vs ?
- ? 和 T 都表示不确定的类型,区别在于我们可以对 T 进行操作,但是对 ?不行
- T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义
- ?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。
3.4 类型通配符 vs 泛型方法
- 类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的类型形参必须在对应方法中显式声明
- 如果某个方法中一个形参(a)的类型或返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符
- 扩展:何时使用泛型方法?何时使用类型通配符?
List<T> 中的 T 是一个形参,可以理解为一个占位符(表示一种引用类型),被使用时,会在程序运行的时候替换成具体的类型,比如替换成String,Integer之类的。
List<?> 中的 ? 是一个实参,这是Java定义的一种特殊类型,比Object更特殊,就像一个影子。比如List<Object>和List<String>是没有父子关系的,这是两个类型,List<Object>类型和List<String>类型;但是List<?> 是 List<String>的父类
References
结束语
碎片时间很难用来做系统化学习,所以纯移动端的内容做到最后都会变成以技术的名义养生:知道了各种奇技淫巧,然而并没有什么用。
当然作为知识树的查漏补缺还是不错的,前提是已经完成了系统学习。