通配符和上下限定

50 阅读4分钟

1. 无界通配符

”?“表示类型通配符,用于代替具体的类型,它只能在<>中使用,可以解决当具体类型不确定的问题。

语法结构:

public void showFlag(Generic<?> generic) {
}

Generic类的定义:

public class Generic<T> {
    private T flag;

    public void setFlag(T flag) {
        this.flag = flag;
    }

    public T getFlag() {
        return this.flag;
    }
}

ShowFlag类,引用generic,用于输出generic的getFlag

public class ShowFlag {
    public void show(Generic<?> generic) {
        System.out.println(generic.getFlag());
    }
}

测试类应用

public class Test {
    public static void main(String[] args) {
        ShowFlag s = new ShowFlag();
        Generic<Integer> generic = new Generic<>();
        generic.setFlag(123);
        s.show(generic);

        Generic<String> generic1 = new Generic<>();
        generic1.setFlag("我是小明");
        s.show(generic1);
    }
}

2. 通配符的上限限定

在java类型中,通配符的上限限定(<? extends 类型>)用于限制泛型的未知类型必须是指定类型的**子类或自身。通过 extends 关键字声明。这种限定能在编译期确保类型安全,同时保留一定的灵活性。

image.png

2.1 核心特性

2.1.1 类型范围限制仅允许接收 Type 及其子类的泛型实例。例如 <? extends Number> 可接收 Generic<Integer>Generic<Double>Generic<Number>,但不能接收 Generic<String>(因 String 不是 Number 子类)。

例如:ShowFlag类

public class ShowFlag {
    public void show(Generic<? extends Number> generic) {
        Number flag = generic.getFlag(); // 可以使用Number类型接收了,比较安全
        System.out.println(flag);
    }
}

使用:

public class Test {
    public static void main(String[] args) {
        ShowFlag s = new ShowFlag();
        Generic<Integer> generic = new Generic<>();
        generic.setFlag(123);
        s.show(generic);

        Generic<String> generic1 = new Generic<>();
        generic1.setFlag("我是小明");
        s.show(generic1); // 会报错,String不是Number的子类
    }
}
2.1.2 “只读” 特性
-   可以**安全读取**:由于确定类型上限,可直接用上限类型接收返回值(如 `Number num = generic.getFlag()`)。
-   不能**写入数据**:编译器无法确定具体类型(可能是 `Integer` 或 `Double` 等),因此禁止调用依赖泛型类型的写入方法(如 `setFlag(? value)` 会报错)。
2.1.3 典型事例
  • 类定义

image.png

  • 上限限定使用

image.png

2.1.4 使用场景

  • 读取优先的操作:如集合 / 泛型类的遍历、统计等(只需获取数据,无需修改)。
  • 限制类型范围:确保操作的对象属于某个继承体系(如处理所有数值类型 Number)。

2.2 上限限定的应用范围

Java 泛型中,上限限定(<? extends 类型> 或 <T extends 类型>)的应用场景非常广泛,可以用于:

  • 泛型类
  • 泛型接口
  • 泛型方法的定义以及方法参数
  • 变量声明

等场景,核心是限制泛型类型的范围(必须是指定类型的子类或自身)。以下是具体应用场景:

2.2.1 泛型类 / 接口的定义(类型参数上限)

在声明泛型类或接口时,可通过 <T extends 类型> 限定类型参数 T 的上限,确保 T 只能是指定类型的子类。

image.png

作用:限制类 / 接口只能处理特定继承体系的类型,避免传入无关类型(如 NumberContainer<String> 会编译报错)。

2.2.2泛型方法的定义(方法类型参数上限)

在泛型方法中,通过 <T extends 类型> 限定方法的类型参数 T,确保方法接收的参数或返回值属于指定范围。

image.png

2.2.3 通配符作为方法参数(<? extends 类型>

在方法参数中使用通配符上限,允许方法接收多种不同但属于同一继承体系的泛型实例,增强方法的灵活性。

image.png

2.2.4变量声明(通配符上限)

声明变量时使用 <? extends 类型>,限制变量只能引用特定范围的泛型实例

image.png

2.2.5多边界上限(extends 类型1 & 类型2

image.png

2.3 总结

通配符上限限定(<? extends 类型>)是控制泛型灵活性与安全性的重要手段,核心作用是限制未知类型的范围,并支持安全读取,适合 “消费型” 操作(只获取数据);

上限限定是表示通配符类型是T类或者T类的子类。

3. 通配符的下限限定

在 Java 泛型中,通配符的下限限定(<? super 类型>)用于限制未知类型必须是指定类型的父类或自身,通过 super 关键字声明。与上限限定(<? extends 类型>)的 “只读” 特性不同,下限限定更侧重 “写入” 操作的安全性。

注意:该方法不适用于泛型类

3.1 基本语法

image.png

3.2 核心特性

3.2.1 类型范围限制

仅允许接收 Type 及其父类的泛型实例。例如 <? super Integer> 可接收 Generic<Integer>Generic<Number>Generic<Object>(因 Number 是 Integer 的父类,Object 是 Number 的父类),但不能接收 Generic<Byte>Byte 是 Integer 的同级类,非父类)。

3.2.2 “写入” 特性
  • 可以安全写入:由于确定类型下限,可向泛型对象中写入 Type 及其子类的实例(编译器确保写入的类型兼容)。
  • 读取限制:读取时只能用 Object 接收(因未知具体类型是下限的哪个父类)。

3.3 示例

  • 类定义

image.png

  • 下限限定使用

image.png

3.4 使用场景

  • 写入优先的操作:如向集合 / 泛型类中添加元素(确保添加的元素类型与下限兼容)。
  • 消费型方法:方法的主要作用是 “向泛型对象写入数据”,而非读取。

3.5 与上限限定的对比

image.png

4. 泛型总结

泛型主要用于编译阶段,编译后的字节码class文件不包含泛型中的类型信息。类型参数在编译后会被替换为Object,运行时虚拟机是不知道泛型的。

使用泛型时几个错误:

  • 基本类型不可以用于泛型
  • 不可以通过类型参数创建对象