美文网首页
Java泛型基础

Java泛型基础

作者: Tinyspot | 来源:发表于2022-07-19 14:17 被阅读0次

    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

    结束语

    碎片时间很难用来做系统化学习,所以纯移动端的内容做到最后都会变成以技术的名义养生:知道了各种奇技淫巧,然而并没有什么用。
    当然作为知识树的查漏补缺还是不错的,前提是已经完成了系统学习。

    相关文章

      网友评论

          本文标题:Java泛型基础

          本文链接:https://www.haomeiwen.com/subject/tyvhirtx.html