文章

Java泛型简介


Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。

著作权归@pdai所有 原文链接:https://pdai.tech/md/java/basic/java-basic-x-generic.html

为什么引入泛型?

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型基本使用

其实大部分时间我们是不会使用到泛型的,一般只有在开发框架或SDK等时,我们才会使用到泛型.因此后面简单介绍泛型的样子,会认即可.

泛型 Class

  • 基本泛型类

    1
    2
    3
    4
    5
    6
    7
    8
    
    class Ponit<T> {
        private T t;
          
        public T getVar() {
            return t;
        }
        ...
    }
    
  • 多元泛型类

    1
    2
    3
    4
    5
    
    class Ponit<T, K> {
        private T t;
        private K k;
        ...
    }
    

泛型 Interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Info<T>{        // 在接口上定义泛型  
    public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型  
}  
class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类  
    private T var ;             // 定义属性  
    public InfoImpl(T var){     // 通过构造方法设置属性内容  
        this.setVar(var) ;    
    }  
    public void setVar(T var){  
        this.var = var ;  
    }  
    public T getVar(){  
        return this.var ;  
    }  
} 

泛型 Method

泛型方法,是在调用方法的时候指明泛型的具体类型。重点看下泛型的方法

1
2
3
4
5
6
7
8
9
/*
 * 泛型方法示例
 * @param <T> 泛型
 * @param t 泛型参数
 * @return 返回泛型参数
 */
public <T> T getObj(Class<T> t) {
    return t;
}

说明:

  • <T> 代表声明此方法拥有一个泛型,如果不带此 <T>那么编译器将无法识别后面的返回值 T 和 参数类型的 Class<T>

  • Class<T> t 参数,必须携带此参数,若不携带,可以编译运行,但是返回时只能返回null,因为我们没有办法在方法内部创建 T类型进行返回. 或携带参数 T t, 我们可以在方法内部对 t进行处理, 然后再返回 t.

泛型的上下限

Java 泛型的上下限(Upper Bounded 和 Lower Bounded)是为了增强泛型的灵活性,允许在泛型类型参数上设置限制。通过上下限,可以控制泛型类型的范围,从而在编译时提供更强的类型安全性。


  1. 泛型上限(Upper Bounded)
  • 泛型上限用于限制类型参数必须是某个类的子类或某个接口的实现类。使用 extends 关键字来定义上限。

    语法:

    1
    
    <T extends 类型>
    

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    public class Box<T extends Number> {
        private T value;
      
        public Box(T value) {
            this.value = value;
        }
      
        public T getValue() {
            return value;
        }
    }
    

    解释:

    • T extends Number 表示 T 必须是 Number 或其子类(如 IntegerDouble 等)。
    • 如果尝试使用非 Number 子类的类型(如 String),编译器会报错。

    使用场景:

    • 当你希望泛型类型参数具有某些共同的行为或方法时(如 Number 的子类都有 intValue() 方法)。
  1. 泛型下限(Lower Bounded)
  • 泛型下限用于限制类型参数必须是某个类的父类或某个接口的超接口。使用 super 关键字来定义下限。

    语法:

    1
    
    <T super 类型>
    

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    public class Box<T> {
        private T value;
      
        public Box(T value) {
            this.value = value;
        }
      
        public void setValue(T value) {
            this.value = value;
        }
      
        public T getValue() {
            return value;
        }
    }
      
    public class Main {
        public static void addNumbers(Box<? super Integer> box) {
            box.setValue(10); // 可以设置 Integer 或其父类(如 Number、Object)
        }
      
        public static void main(String[] args) {
            Box<Number> numberBox = new Box<>(0);
            addNumbers(numberBox); // 合法,因为 Number 是 Integer 的父类
        }
    }
    

    解释:

    • ? super Integer 表示类型参数必须是 Integer 或其父类(如 NumberObject)。
    • 适合用于写入操作(如 setValue),因为可以安全地将子类对象赋值给父类引用。

    使用场景:

    • 当你希望泛型类型参数可以接受某个类的父类时(如集合的写入操作)。
  1. 上下限的结合使用
  • 在实际开发中,上下限可以结合使用,以实现更灵活的类型控制。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    public class Utils {
        // 上限:T 必须是 Number 或其子类
        public static <T extends Number> double sum(List<T> numbers) {
            double sum = 0.0;
            for (T number : numbers) {
                sum += number.doubleValue();
            }
            return sum;
        }
      
        // 下限:T 必须是 Integer 或其父类
        public static void addIntegers(List<? super Integer> list) {
            list.add(10);
            list.add(20);
        }
    }
    

    解释:

    • sum 方法要求传入的列表元素必须是 Number 或其子类。
    • addIntegers 方法要求传入的列表元素必须是 Integer 或其父类。
  1. 通配符上下限
  • 在泛型中,通配符 ? 可以与上下限结合使用,表示未知类型。

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    public class Main {
        // 上限:List 的元素必须是 Number 或其子类
        public static void printNumbers(List<? extends Number> numbers) {
            for (Number number : numbers) {
                System.out.println(number);
            }
        }
      
        // 下限:List 的元素必须是 Integer 或其父类
        public static void addIntegers(List<? super Integer> list) {
            list.add(10);
            list.add(20);
        }
      
        public static void main(String[] args) {
            List<Integer> integers = Arrays.asList(1, 2, 3);
            List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);
            List<Number> numbers = new ArrayList<>();
      
            printNumbers(integers); // 合法
            printNumbers(doubles); // 合法
      
            addIntegers(numbers); // 合法
        }
    }
    

    解释:

    • ? extends Number 表示列表元素可以是 Number 或其子类。
    • ? super Integer 表示列表元素可以是 Integer 或其父类。

  1. 总结
  • 泛型上限(extends

    • 用于限制类型参数必须是某个类的子类或某个接口的实现类。
    • 适合读取操作(如 getValue)。
  • 泛型下限(super
    • 用于限制类型参数必须是某个类的父类或某个接口的超接口。
    • 适合写入操作(如 setValue)。
  • 通配符上下限

    • 结合 ? 使用,表示未知类型。
    • 增强泛型的灵活性。
  • 多个条件:

    • 结合 &

      1
      
      public static <T extends Staff & Passenger> void discount(T t)
      

泛型数组

原文

[Java 基础 - 泛型机制详解Java 全栈知识体系](https://pdai.tech/md/java/basic/java-basic-x-generic.html)
本文由作者按照 CC BY 4.0 进行授权