重识java ——泛型基础

加油.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 {

    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 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 {

    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 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  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  Result makeResult(U first,V second){
        Result pair = new Result<>(first, second);
        return result;
    }

//调用:
Result result = makeResult("张少林", 25);

泛型接口

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

public interface Comparable {
      public int compareTo(T o);
}

public interface Comparator {
      ......
}

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

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

限定类型参数

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

  1. 上界为具体类
public class Pair {

    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 extends Pair{


    public NumberPair(U first, V second) {
        super(first, second);
    }

    public static void main(String[] args) {
        NumberPair numberPair = new NumberPair<>(666, 999);
        System.out.println(numberPair.getFirst() + "," + numberPair.getSecond());
//结果是:666,999
     //编译错误
        new NumberPair("cdsc","csdc")
    }
}

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

  1. 上界为接口

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

    public static > 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   void addAll(List c){
        for (int i = 0; i < c.size(); i++) {
            add(c.get(i));
        }
    }

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

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

张少林同学.jpg

你可能感兴趣的:(重识java ——泛型基础)