重识java ——泛型基础

作者: 张少林同学 | 来源:发表于2018-03-12 20:14 被阅读142次
    加油.png

    做过 java/Android 的童鞋都知道,不管我们后台写 json 接口返回,还是移动端解析 json 接口,我们会将最终结果做统一的格式处理,具体代码可以是这样的:

    public class Result {
        
        private Integer code; //状态码
        private String msg;//消息
        private Object data;//最终返回的数据体
        // ...省略 set get 方法
    }
    

    最终调用如下:

    Result result = new Result();
    result.setCode(200);
    result.setMsg("成功");
    result.setData("我是最终数据结果");
    //执行序列化,反序列化操作
    

    分析:

    优点:看到 Object 上帝类,就知道最终 data 这个参数可以设置任意类型参数,对于数据格式固定,这种写法明显降低了代码量,对最终结果做了统一处理。
    缺点:我们试图获取构造的对象数据时,有可能不清楚当初传入的是什么类型,强制类型转换为其他类型,或者由于程序员的疏忽而强制转换为其他类型,这在编译时是正常通过的,因为 Object 可以强制转换为任意类型,只是对象中我们传入的类型有可能不能强制转换,最终在运行时,由 java 抛出类型转换异常,这无疑是一种程序潜在的不安全。如下代码:

    //编译正常,运行时抛异常
     Integer data = (Integer) result.getData();
     System.out.println("data:" + data);
    

    实际传入为 String ,强制转为 Integer,抛出 ClassCastException 异常。

    那么,基于此,java 提供的泛型就是为了解决这个痛点的。

    泛型特点

    泛型是计算机程序中一种重要的思维方式,它将数据结构和算法与数据类型分离开,使得同一套数据结构和算法能够应用于各种数据类型,而且可以保证类型安全,提高可读性。

    普通泛型类

    用泛型对 Result 类做下修改:

    public class Result<T> {
    
        private Integer code; //状态码
        private String msg;//消息
        private T data;//数据体
    
        // ...省略 部分 set,get 方法
        public T getData() {
            return data;
        }
        public void setData(T data) {
            this.data = data;
        }
    
        public static void main(String[] args) {
            //指定泛型为 String
            Result<String> result = new Result();
            result.setCode(200);
            result.setMsg("成功");
            result.setData("我是最终数据结果");
            //不需要类型转换,直接返回泛型类型
            String data = result.getData();
            System.out.println("data:" + data);
        }
    }
    

    分析:

    1. 取消 Object ,改用 T 类型参数,泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入
    2. 创建对象的时候,指定了泛型类型,最终获取数据时,java 编译器在内部会帮助识别类型,不需要类型转换,在编译时期就把类型安全解决掉,不会有后期的安全隐患。

    多参数类型参数

    类型参数可以有多个,用逗号隔开,简单改写代码如下:

    public class Result<T,U> {
    
        private Integer code; //状态码
        private String msg;//消息
        private T data1;
        private U data2;
    
        // ...省略部分 set get 方法
    
        public T getData1() {
            return data1;
        }
        public U getData2() {
            return data2;
        }
        public static void main(String[] args) {
    
            Result<String,String> result = new Result();
            result.setCode(200);
            result.setMsg("成功");
            result.setData1("我是第一个结果");
            result.setData2("我是第二个结果");
            String data1 = result.getData1();
            String data2 = result.getData2();
            System.out.println("data1:" + data1 + ",data2:" + data2);
        }
    }
    

    泛型方法

    泛型也可以指定在具体的方法上,与所在的类是否是泛型没关系,如下,添加静态方法:
    计算:找出 泛型数组中,指定元素的索引值,找不到则返回 -1

    //查找数组指定元素索引
        public static <T> int indexOf(T[] arr,T element){
            for (int i = 0; i < arr.length; i++) {
                if (arr[i].equals(element)){
                    return i;
                }
            }
            return -1;
        }
    

    调用如下:

     int index = indexOf(new Integer[]{1, 3, 5}, 10);
      System.out.println(index);
      //结果为:-1
    
      int index1 = indexOf(new String[]{"张少林", "福建", "漳州", "25"}, "张少林");
      System.out.println(index1);
      //结果:0
    

    以上可知,泛型方法与泛型类达到的效果一致,同一段代码,与所传入的参数类型无关,可以方便的在各种数据类型之间进行复用,且是类型安全的。

    同泛型类一样,泛型方法也可以传入多个类型参数,用户最终调用不需要关心传入的具体类型是啥,java 编译器会自动处理。代码如下:

     public static <U,V> Result<U,V> makeResult(U first,V second){
            Result<U, V> pair = new Result<>(first, second);
            return result;
        }
    
    //调用:
    Result<String, Integer> result = makeResult("张少林", 25);
    

    泛型接口

    接口也可以是泛型的,java 中的 Comparable<T>,Comparator<T> 就是泛型接口,定义如下:

    public interface Comparable<T> {
          public int compareTo(T o);
    }
    
    public interface Comparator<T> {
          ......
    }
    

    实现 接口,必须制定泛型类型,如:Integer 类 指定泛型类型为 Integer

    public final class Integer extends Number implements Comparable<Integer> {
        public int compareTo(Integer anotherInteger) {
            return compare(this.value, anotherInteger.value);
        }
    }
    

    限定类型参数

    之前的泛型写法中,类型参数默认是派生自 Object 的,这时候,类型参数传入任何类型都是可以的,而我们可以自定义类型参数的上界,类型参数上界可以是个具体类,接口,其他类型参数,此时类型参数必须为上界类型的子类或者是它本身,或者实现了上界接口。

    1. 上界为具体类
    public class Pair<U,V> {
    
        U first;
        V second;
    
        public Pair(U first, V second) {
            this.first = first;
            this.second = second;
        }
    
        public U getFirst() {
            return first;
        }
    
        public V getSecond() {
            return second;
        }
    }
    
    public class NumberPair<U extends Number,V extends Number> extends Pair<U,V>{
    
    
        public NumberPair(U first, V second) {
            super(first, second);
        }
    
        public static void main(String[] args) {
            NumberPair<Integer, Integer> numberPair = new NumberPair<>(666, 999);
            System.out.println(numberPair.getFirst() + "," + numberPair.getSecond());
    //结果是:666,999
         //编译错误
            new NumberPair<String,String>("cdsc","csdc")
        }
    }
    
    

    很明显,假如传入的类型参数,不是派生自 Number 类,在编译时,就会提示错误。

    1. 上界为接口

    泛型方法中,类型参数必须实现某个接口,从而依赖接口的方法,具体例子如下,获取数组中的最大值,具体类型依赖 Comparable<T> 接口的方法,compareTo,也就是每个具体类型实现 compareTo 的比较逻辑即可:

        public static <T extends Comparable<T>> T max(T[] arr){
            T max = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if (arr[i].compareTo(max)>0) {
                    max = arr[i];
                }
            }
            return max;
        }
    
    1. 上界为其他类型参数,在不知道类型参数具体的上界是什么的时候,可以定义为其他的类型参数,类似如下代码:
    public static  <T extends E> void addAll(List<T> c){
            for (int i = 0; i < c.size(); i++) {
                add(c.get(i));
            }
        }
    

    至此,java 泛型基础总结先到这里了,还没完,路还很长。

    更多原创文章会在公众号第一时间推送,欢迎扫码关注 张少林同学

    张少林同学.jpg

    相关文章

      网友评论

        本文标题:重识java ——泛型基础

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