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

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

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

相关推荐
959y40 分钟前
[Go 微服务] Kratos 使用的简单总结
开发语言·golang·kratos
虫小宝1 小时前
如何在Java中实现批量数据处理
java·开发语言
PeterClerk1 小时前
基于Pygame的贪吃蛇小游戏实现
开发语言·python·pygame
king888866661 小时前
Java中的AQS
java
冰暮流星1 小时前
软设之类的继承与泛化,多重继承
java·开发语言
虫小宝1 小时前
Java中的多线程与并发编程详解
java·开发语言
oNuoyi1 小时前
定位线上同步锁仍然重复扣费的Bug定位及Redis分布式锁解决方案
java·spring boot·redis·分布式
Easonmax1 小时前
【C++】 解决 C++ 语言报错:Undefined Reference
java·开发语言·c++
Lightning-py1 小时前
Python使用(...)连接字符串
开发语言·python
计算机平台作业答案讲解1 小时前
QT实现GIF动图显示(小白版,可直接copy使用)
服务器·开发语言·数据结构·数据库·c++·qt·动态规划