泛型 基础

104 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

泛型

Java 中有 8 种基本数据类型和类(自定义数据类型),类之间可以组合或者继承

而正是由于我们更加关注的是能力,而不是类,所以有了接口的概念,它不仅可以复用代码,而且可以降低代码耦合,提高灵活性

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

例子

public class test01 {
    public static void main(String[] args) {
        Pair<Integer> minmax = new Pair<Integer>(1,100);
        Integer min = minmax.getFirst();
        Integer max = minmax.getSecond();
        System.out.println(min);
        System.out.println(max);
        Pair<String> kv = new Pair<String>("name","李李");
        String kv1 = kv.getFirst();
        String kv2 = kv.getSecond();
        System.out.println(kv1);
        System.out.println(kv2);
    }
}

class Pair<T>{
    T first;
    T second;
    public Pair(T first, T second){
       this.first = first;
       this.second = second;
    }
    public T getFirst(){
        return first;
    }
    public T getSecond(){
        return second;
    }
}

//结果
1
100
name
李李

上面例子中的就是一个泛型类,可以发现传入 Integer 时候,T 就代表 Integer 进行处理,传入 String 时候,T 就代表 String 进行处理。数据类型作为参数传入,然后通过泛型类中的代码逻辑操作数据

public class test01 {
    public static void main(String[] args) {
        Pair1<String, Integer> res = new Pair1<String, Integer>("age:", 12);
        String res1 = res.getFirst();
        Integer res2 = res.getSecond();
        System.out.println(res1 + res2);
    }
}

class Pair1<X, Y>{
    X first;
    Y second;

    public Pair1(X first, Y second){
        this.first = first;
        this.second = second;
    }

    public X getFirst(){
        return first;
    }

    public Y getSecond(){
        return second;
    }
}

//结果
age:12

上面例子可以发现,泛型的类型参数可以是多个,并且类型不一样

原理

底层原理其实就是使用 Object,如果一个类是泛型类,Java 编译器会将泛型代码转换为非泛型代码,也就是将类型参数 T 替换为 Object,插入必要的强制类型转换。

PS:Java 程序运行分为两个阶段,第一阶段是编译器将 Java 源代码转换为 class 文件,第二阶段是在 JVM 中运行 class 文件。而泛型是在编译期完成的,所以 JVM 执行的时候不知道泛型的事儿,运行的就是普通类的代码

public class test1 {
    public static void main(String[] args) {
        Pair minMax = new Pair(1, 100);
        Integer min = (Integer) minMax.getFirst();
        Integer max = (Integer) minMax.getSecond();
        Pair kv = new Pair("age", 12);
        String age = (String) kv.getFirst();
        Integer num = (Integer) kv.getSecond();
        System.out.println(min);
        System.out.println(max);
        System.out.println(age);
        System.out.println(num);
    }
}
class Pair{
    Object first;
    Object second;
    public Pair(Object first, Object second){
        this.first = first;
        this.second = second;
    }

    public Object getFirst(){
        return first;
    }

    public Object getSecond(){
        return second;
    }
}

泛型是通过擦除来实现的,程序运行过程中,不知道泛型的实际类型参数,比如 Pair,运行中只知道 Pair,不知道 Integer

好处

  1. 安全性

有可能类型搞错了,如果不使用泛型,编译期没有问题,运行期出现问题

Pair pair = new Pair("age", 212);
Integer num = (Integer) pair.getFirst();
String age = (String) pair.getSecond();

如果使用泛型

Pair<String, Integer> pair = new Pair<>("age",1);
Integer num = pair.getFirst();//有编译错误
String age = pair.getSecond();//有编译错误
  1. 可读性

用途

泛型类:容器类

容纳并管理多项数据的类,动态数组举例

import java.util.Arrays;

public class DynamicArray <E>{
    private static final int DEFAULT_CAPACITY = 10;
    private int size;
    private Object[] elementData;
    public DynamicArray(){
        this.elementData = new Object[DEFAULT_CAPACITY];
    }
    private void ensureCapacity(int minCapacity){
        int oldCapacity = elementData.lengt;
        if(oldCapacity >= minCapacity){
            return;
        }
        int newCapacity = oldCapacity * 2;
        if(newCapacity < minCapacity){
            newCapacity = minCapacity;
        }
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    public void add(E e){
        ensureCapacity(size + 1);
        elementData[size++] = e;
    }
    
    public E get(int index){
        return (E) elementData[index];
    }
    public int size(){
        return size;
    }
    
    public E set(int index, E element){
        E oldValue = get(index);
        elementData[index] = element;
        return oldValue;
    }
}

使用

import java.util.Random;

public class training {
    public static void main(String[] args) {
        DynamicArray<Double> arr = new DynamicArray<>();
        Random rnd = new Random();
        int size = 1 +rnd.nextInt(100);
        for(int i = 0; i < size; i++){
            arr.add(Math.random());
        }
        Double d = arr.get(rnd.nextInt(size));
        System.out.println(d);
    }
}

注意:泛型中可以嵌套泛型类,例如

DynamicArray<Pair<Integer, String>> arr = new DynamicArray<>();

泛型方法

首先一个方法是否是泛型,和它所在的类是否是泛型的没有任何关系,可以所在类不是泛型,而方法是泛型,也可以所在类是泛型,方法也是泛型

public static <T> int indexOf(T[] arr, T elm){
    for(int i=0; i<arr.length; i++){
        if(arr[i].equals(elm)){
        return i;
        }
    }
    return -1;
}

调用

indexOf(new Integer[]{1,3,5}, 10)
indexOf(new String[]{"hello","workld"}, "OK")

泛型接口

public interface Comparable<T> {
    public int compareTo(T o);
}
public final class Integer extends Number implements Comparable<Integer>{
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
//其他代码
}
private static class CaseInsensitiveComparator implements Comparator<String> {
    public int compare(String s1, String s2) {
        //主体代码
    }
}