Java——泛型

前言:

泛型类,泛型方法,泛型接口,通配符,类型擦除


文章目录

一、 泛型

Java泛型(Java Generics)是一种允许类、接口和方法操作不同数据类型而无需指定具体数据类型的机制。Java引入泛型是为了提供更高的类型安全性、可读性、可维护性和代码复用性,这在Java 5及以后版本中引入。

1.1、泛型的基本概念

  1. 泛型类

    泛型类是指在类声明时带有一个或多个类型参数的类。这些类型参数在类的使用过程中被具体化。定义泛型类时,使用尖括号<>括起来的类型参数。

    java 复制代码
    public class Box<T> {
        private T content;
    
        public void set(T content) {
            this.content = content;
        }
    
        public T get() {
            return content;
        }
    }

    在上述例子中,T是一个类型参数,可以在使用时被具体化为任何类型。

  2. 泛型接口

    泛型接口和泛型类类似,只是定义在接口上。

    java 复制代码
    public interface Container<T> {
        void add(T item);
        T remove();
    }
  3. 泛型方法

    泛型方法是在方法声明中引入一个或多个类型参数的方法。这些类型参数使方法可以独立于任何特定的类型工作。

    java 复制代码
    public class Util {
        public static <T> boolean compare(T t1, T t2) {
            return t1.equals(t2);
        }
    }

    在这个例子中,<T>是在方法声明中引入的类型参数,可以在方法的参数列表和返回类型中使用。

注意,泛型方法和泛型类中的普通方法有以下区别:

  1. 类型参数的定义位置不同

    • 泛型方法:类型参数是在方法声明中定义的,方法可以在其签名中使用这些类型参数。
    • 类中的普通方法:如果类是泛型类,那么普通方法可以使用类的类型参数,但不能定义自己的类型参数。
  2. 泛型方法的灵活性

    • 泛型方法:可以在非泛型类中定义,也可以在泛型类中定义。每个方法可以有自己独立的类型参数。
    • 类中的普通方法:只能使用类级别的类型参数(如果类是泛型类)。

在泛型类中定义泛型方法的示例

在泛型类中也可以定义泛型方法,且方法可以有不同的类型参数:

java 复制代码
public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }

    // 泛型方法,使用独立的类型参数 U
    public <U> void print(U item) {
        System.out.println(item);
    }
}

1.2 泛型的使用

  1. 泛型类的使用

    在实例化泛型类时,需要为类型参数提供具体的类型。

    java 复制代码
    Box<String> stringBox = new Box<>();
    stringBox.set("Hello");
    String content = stringBox.get();
    System.out.println(content);
  2. 泛型方法的使用

    调用泛型方法时,可以显式指定类型参数,也可以让编译器通过类型推断自动确定类型参数。

    java 复制代码
    String s1 = "test";
    String s2 = "test";
    boolean result = Util.<String>compare(s1, s2); // 显式指定类型参数
    boolean result2 = Util.compare(s1, s2); // 通过类型推断
  3. 泛型接口的使用

    实现泛型接口时,可以指定接口的类型参数。

    java 复制代码
    public interface Container<T> {
        void add(T item);
        T remove();
    }
    
    // 实现泛型接口时具体化类型参数为 String
    public class StringContainer implements Container<String> {
        private List<String> items = new ArrayList<>();
    
        @Override
        public void add(String item) {
            items.add(item);
        }
    
        @Override
        public String remove() {
            return items.isEmpty() ? null : items.remove(0);
        }
    }

    或者实现类保留类型参数

    java 复制代码
    public interface Container<T> {
        void add(T item);
        T remove();
    }
    
    // 让实现类保留类型参数
    public class GenericContainer<T> implements Container<T> {
        private List<T> items = new ArrayList<>();
    
        @Override
        public void add(T item) {
            items.add(item);
        }
    
        @Override
        public T remove() {
            return items.isEmpty() ? null : items.remove(0);
        }
    }

三、通配符(Wildcard)

  1. 无界通配符
    <?> 表示任何类型。这在需要表示不确定的类型时很有用。

    java 复制代码
    public void printList(List<?> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }
  2. 上界通配符
    <? extends T> 表示类型是 TT 的子类。

    java 复制代码
    public void processElements(List<? extends Number> list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }
  3. 下界通配符
    <? super T> 表示类型是 TT 的父类。

    java 复制代码
    public void addNumbers(List<? super Integer> list) {
        list.add(1);
        list.add(2);
        list.add(3);
    }

四、类型擦除(Type Erasure)

在Java中,泛型类型在编译时会被擦除,也就是Java编译器将泛型代码转换为非泛型代码,这意味着在运行时,泛型类型信息将不可用。这是为了向后兼容Java 5之前的版本。在类型擦除过程中:

  1. 泛型类型参数被替换为其上界(若未指定上界,则为Object)。
  2. 插入类型转换,以保证类型安全。
java 复制代码
public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
    // 编译后,大致变成以下形式
    // private Object content;
    // public void set(Object content) { this.content = content; }
    // public Object get() { return content; }
}

五、泛型的局限性

尽管泛型提供了很多好处,但它也有一些限制:

  1. 不能实例化泛型类型:因为类型擦除的存在,无法在运行时获取类型参数的具体类型。

    java 复制代码
    public class Box<T> {
        // 错误:不能实例化类型参数
        // private T content = new T();
    }
  2. 不能创建泛型数组:数组在运行时会保留类型信息,而泛型在运行时会被擦除。

    java 复制代码
    public class Box<T> {
        // 错误:不能创建泛型数组
        // T[] array = new T[10];
    }
  3. 不能使用基本类型作为类型参数:泛型只能用于引用类型。

    java 复制代码
    Box<int> intBox = new Box<>(); // 错误:不能使用基本类型作为类型参数
    Box<Integer> integerBox = new Box<>(); // 正确
  4. 不能在静态上下文中使用泛型类型参数:因为静态成员属于类,而不属于某个特定的对象。

    java 复制代码
    public class Box<T> {
        // 错误:静态字段不能使用类型参数
        // private static T content;
    }

如果你喜欢这篇文章,点赞👍+评论+关注⭐️哦!

欢迎大家提出疑问,以及不同的见解。

相关推荐
IU宝2 分钟前
C/C++内存管理
java·c语言·c++
湫ccc2 分钟前
《Python基础》之pip换国内镜像源
开发语言·python·pip
瓜牛_gn3 分钟前
依赖注入注解
java·后端·spring
fhvyxyci3 分钟前
【C++之STL】摸清 string 的模拟实现(下)
开发语言·c++·string
hakesashou4 分钟前
Python中常用的函数介绍
java·网络·python
qq_459730035 分钟前
C 语言面向对象
c语言·开发语言
佚先森13 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
菜鸟学Python14 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
一个小坑货21 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2726 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust