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;
    }

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

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

相关推荐
Source.Liu10 分钟前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng10 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马13 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng16 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
七星静香17 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
Jacob程序员18 分钟前
java导出word文件(手绘)
java·开发语言·word
ZHOUPUYU19 分钟前
IntelliJ IDEA超详细下载安装教程(附安装包)
java·ide·intellij-idea
stewie622 分钟前
在IDEA中使用Git
java·git
小白学大数据25 分钟前
正则表达式在Kotlin中的应用:提取图片链接
开发语言·python·selenium·正则表达式·kotlin
VBA633727 分钟前
VBA之Word应用第三章第三节:打开文档,并将文档分配给变量
开发语言