avatar

一条在知识海洋的咸鱼

这个家伙很懒,啥也没有留下😋

  • 首页
  • Linux
  • OCJP
  • Java核心技术卷
  • J2EE相关标准
  • 深入理解Java虚拟机
  • NIO与SOcket编程技术指南
  • Java多线程编程核心技术
  • Redis开发与运维
  • Spring Cloud Alibaba 微服务原理与实践
  • DevOps
  • Docker
  • MySQL必知必会
  • AI自学路线
  • Spring Boot 编程思想(核心篇)
主页 第六章 范型
文章

第六章 范型

发表于 2021-05-7 更新于 2026-06- 10
作者 Administrator
18~23 分钟 阅读

创建和使用泛型类

  • 范型
  • 菱形<>语法
  • 范型类
    • 范型好处举例
  • 范型方法
  • 通配符
    • 无界通配符<?>
    • 有界通配符? extends T ? super T
      • 上限通配符
      • 下界通配符
  • 范型限制

范型

java5中添加了泛型作为类型检查的机制,提前告知类型,避免强转

  • 泛型是编译器处理的,在运行时,Java 不知道泛型。编译器验证您使用的类型是否正确,然后使用 java.lang.Object类型
    在运行时,List < string > 和 List < integer > 是相同的,因为类型信息已经被编译器删除了(它们只被看作 List)。且只能处理包装类型,不能处理基本类型

菱形<>语法

Java 7以来,不再在赋值的两边指定泛型类型

范型类

为类和接口定义泛型类型。这只是在类(或接口)名称旁边声明一个类型参数的问题

  • List接口的源码
public interface List<E> extends Collection<E> {
    boolean add(E e);
    Iterator<E> iterator();
}

E只是一个标识符,就像一个命名的变量。它可以是你想要的任何东西。但是,约定是使用单个大写字母。

  • 一些常见的字母:
    E 元素
    K 键
    V 值
    T 数据类型

范型好处举例

class Holder {
    private String s;
    public Holder(String s) {
        this.s = s;
    }
    public String getObject() {
        return s;
    }
    public void printObject() {
        System.out.println(s);
    }
}

class IntegerHolder {
    private Integer s;
    public IntegerHolder(Integer s) {
        this.s = s;
    }
    public Integer getObject() {
        return s;
    }
    public void printObject() {
        System.out.println(s);
    }
}

逻辑是类似的,但是不同类型处理需要写很多方法
添加范型则不同

class Holder<T> {
    private T t;
    public Holder(T t) {
        this.t = t;
    }
    public T getObject() {
        return t;
    }
    public void printObject() {
        System.out.println(t);
    }
}
Holder<String> h1 = new Holder<>("Hi");
Holder<Integer> h2 = new Holder<>(1);
String s = h1.getObject();

泛型类型 t 在类中的任何地方都可以使用,不受制于数据类型

范型方法

还可以在任何方法中声明类型参数

class Utils {
    public static <T> void print(T t) {
        System.out.println(t);
    }

    <T> void genericMethod1(List<T> list) {
    }

    <T, U> T genericMethod2(U u) {
        T t = null;
        return t;
    }
}

通配符

  • 范型的局限性
    在使用泛型时,不能将派生类型分配给基类型; 两种类型应该是相同的(显式地或使用菱形运算符)。
    image.png
    换个角度List < Object > 类型的列表可以保存 Object 及其子类的实例。换句话说,列表可以包含任何对象类型,而不仅仅是字符串。例如,您可以有一个字符串和整数的列表,这明显违反了类型安全。

无界通配符<?>

无界通配符类型(< ? >)意味着列表的类型是未知的,所以它可以匹配任何类型
以在某种程度上考虑列表 < ? > 作为所有 List 的超类,因为您可以分配任何类型的 List
image.png

  • 因为编译器不能推断元素的类型,所以不能保证类型安全(只能插入 null,因为它没有类型)
    image.png
    但是当使用在方法参数中时,这些方法的代码只使用泛型类的方法或者来自 Object 的方法,而不是特定类型的方法
// You can pass any type of List here
int getSize(List<?> list) {  
    return list.size();
}

但是当使用类型参数时,我们只能使用 Object 中的方法,因为我们不知道类型参数的确切类型

有界通配符? extends T ? super T

class Printer<T> {
   public void print(T t) {
      System.out.println(t.toUpperCase());// Error
      // What if T doesn't represent a String?
   }
}
  • ? extends T (上限通配符)
  • ? super T (下限通配符)
    通过使用这些通配符,放松泛型所施加的限制。这还允许使用泛型的多态性或子类型

上限通配符

class Printer<T extends String> {
   public void print(T t) {
      System.out.println(t.toUpperCase());//OK!
   }
}
	List<Object> list = new ArrayList<String>(); // Error
        List<? extends Object> list2 = new ArrayList<String>(); // OK!
	list2.add("Hi"); // Compile-time error

但是编译器仍然不能确定列表将包含什么类型(我们可以添加任何类型)

List<Integer> listInteger = new ArrayList<>();
        List<Float> listFloat = new ArrayList<>();
        List<Number> listNumber = new ArrayList<>();
        listNumber.add(new Integer(1)); // OK
        listNumber.add(new Float(1.0F)); // OK
        listNumber = listInteger; // Error
        listNumber = listFloat; // Error

        List<? extends Number> listExtendsNum = new ArrayList<>();
        // This would cause an error
        listExtendsNum.add(new Integer(1));
        listExtendsNum = listInteger; // OK
        listExtendsNum = listFloat; // OK

下界通配符

List<? super Integer> list = new ArrayList<>();

这意味着 List 可以分配给 Integer 列表(List < Integer >)或者 Integer 的某个超类型(比如 List < number > 或 List < object >)。

List<? super Integer> list = new ArrayList<>();
list.add(1); // OK!
list.add(2); // OK!

我们可以添加 t 或其中一个子类的实例,因为它们也是 t
关于集合范型通配符需要考虑的是:

  • 一件事是你可以分配什么,
  • 另一件事是你可以添加什么
    上限通配符可以使用约束边界的对象的方法
    下限通配符<? super Integer>可以加入所有Integer及其超类的对象类型

范型限制

  • 泛型不适用于基本类型
  • 不能实例化范型
class Test<T> {
   T var = new T();//ERROR
   // You don't know the type's constructors
}
  • 不能声明类型参数的静态字段
  • 不能对泛型类型使用 instanceof,由于类型擦除
  • 不能实例化泛型类型的数组
  • 不能创建、捕获或抛出泛型类型,但是,可以在 throws 子句中使用类型参数
  • 不能重载一个方法,类型擦除将留下相同类型的参数,编译报错
OCJP
许可协议: 
分享

相关文章

9月 6, 2021

关于这篇OCP-Java专题说明

5月 28, 2021

三十章 本地化

通过使用 Locale 对象读取和设置 Locale。创建并读取属性文件。为每个区域设置构建资源包,并在应用程序中加载资源包。本地化设置语言环境本地化设计一个能够处理不同语言和地区的应用程序。最常见的是消息、日期和数字。java.util.LocaleLocale类基本上代表一种语言和一个国家获得机

5月 27, 2021

第二十九章 JDBC API

了解JDBC API 核心的接口,包括 Driver、 Connection、 Statement 和 ResultSet 接口及其与提供者实现的关系。使用 DriverManager 类(包括 JDBC URL)标识连接到数据库所需的组件。从数据库提交查询和读取结果,包括创建语句、返回结果集、遍历

下一篇

第七章 集合

上一篇

第五章 枚举

最近更新

  • 55 异常兜底:工具失败、非法输出、空检索与超时
  • 54 Agent Memory:短期记忆、用户上下文与摘要
  • 53 Human-in-the-loop:让关键动作暂停、确认与恢复
  • 52 工单 Tool:字段校验、创建结果与失败补偿
  • 51 订单 Tool:Mock、鉴权、状态解析与脱敏

热门标签

java基础 微服务 maven Spring Tomcat DDD Linux Linux基础 SQL基础 数据结构算法

目录

©2026 一条在知识海洋的咸鱼. 保留部分权利。

使用 Halo 主题 Chirpy