文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java 中泛型 T 和 ? 的区别详解

2024-04-02 19:55

关注

泛型中 T 类型变量 和 ? 通配符 区别

定义不同 :T 是类型变量,? 是通配符

使用范围不同:

通常我们使用 ? 的时候并并不知道也不关心这个时候的类型,这里只想使用其通用的方法,而且 ? 通配符是无法作用于声明类的类型参数,一般作用于方法和参数上。而 类型变量 T 在类定义时具有更广泛的应用。

在某些程度的使用上 ? 通配符与 T 参数类型是可以等效的,但是 T 参数类型并不支持下界限制 即 T super SomeTing 而 通配符支持 ? super SomeThing

如果你想写一个通用的方法且该方法的逻辑不关心类型那么就大胆的用 ? 通配符来进行适配和限制吧,如果你需要作用域类型(这可能在操作通用数组类型时更明显)或者声明类的类型参数时请使用 T 类型变量

类型参数定义了一种代表作用域类型的变量(例如,T),通配符只是定义了一组可用于泛型类型的允许类型。通配符的意思是“在这里使用任何类型”

在泛型的使用中我们经常可以看到这样的用法:

public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}
List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. List<? extends Integer> is a subtype of List<? extends Number>
public interface GenericProgressiveFutureListener<F extends ProgressiveFuture<?>> extends GenericFutureListener<F> {
    void operationProgressed(F future, long progress, long total) throws Exception;
}

如果你其用法和概念仍有疑问,那不妨继续阅读本文

了解他们的概念:Generic Types 和 Wildcards ,以及使用。

Generic Types 类型变量

通用类型即 T、F、K、V 这样的写法,它是一种是通过类型参数化的通用类或接口,也可以称之为 类型变量

类型变量可以是任何非原始类型:任何类类型、任何接口类型、任何数组类型,甚至是另一个类型变量

按照惯例,类型参数名称是单个大写字母。

最常用的类型参数名称是:

用法

1.声明通用的类型 – 泛型类:

当我们想对通用的对象类型进行操作时我们可能想到使用 Object ,但是使用 Object 在编译时无法进行检查,因为 Object 是所有类的父类,这可能导致我们意图在传入 Integer 并可以取出 Inerger 时,在另一部分代码错误的传入了 String

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

为了避免上述的问题,我们可以选择使用 类型变量

public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

你也可以使用多个类型参数

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
    this.key = key;
    this.value = value;
    }

    public K getKey()    { return key; }
    public V getValue() { return value; }
}

2.声明通用的方法 – 泛型方法:

泛型方法 是引入自己的类型参数的方法。这类似于声明泛型类型,但类型参数的范围仅限于声明它的方法。允许静态和非静态泛型方法,以及泛型类构造函数。

泛型方法的语法包括一个类型参数列表,在尖括号内,它出现在方法的返回类型之前。对于静态泛型方法,类型参数部分必须出现在方法的返回类型之前。

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public static <T> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

一个完整的调用是

JestTestMain.<String>printListT(names);

但是通常可以省略类型,这里使用到的功能是 类型推断

JestTestMain.printListT(names);

有界类型参数

同时我们可以对类型参数进行限制通过 extends 关键字

如 <T extends Number> 这里的泛型参数就限制了必须继承于 Number 类。

public static <T extends Number> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

同时 Java 也支持多重限定,如 <T extends CharSequence & Comparable<T> & Serializable> 但是如果其中限定包含 类 需要写在最前面

public static <T extends CharSequence & Comparable<T> & Serializable> void printListT(List<T> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

Wildcards 通配符

通配符即指 ?

在泛型代码中,? 表示未知类型。通配符可用于多种情况:

作为参数、字段或局部变量的类型,有时作为返回类型(但请避免这样做)。

通配符从不用作泛型方法调用、泛型类实例创建或超类型的类型参数。

用法

通配符分为 3 种:

1.上界通配符:? extend 上界类型

List public static void process(List list) { }

我们可以使用上界通配符来放宽对变量的限制

2.无界通配符:?

如 List<?> 这表示未知类型的列表,一般有两种情况下无界通配符是有用的:

如何理解这句话的意思呢?来看一个例子:

public static void printList(List<Object> list) {
    for (Object elem : list)
        System.out.println(elem + " ");
    System.out.println();
}

printList 的意图是想打印任何类型的列表,但是它没有达到目标,其只打印了 Object 实例的列表。它不能打印 List<Integer>、List<String>、List<Double>等,因为它们不是 List<Object> 的子类型。

编译时将会报错。

这里我们换成通配符将正确运行

public class JestTestMain {
    public static void main(String[] args) {
        List<String> names= Lists.newArrayList();
        names.add("张三");
        names.add("张三1");
        names.add("张三2");
        printList(names);
    }

    public static void printList(List<?> list) {
        for (Object elem : list)
            System.out.println(elem + " ");
        System.out.println();
    }
}

打印:

张三 
张三1 
张三2 

这里需要明白的一点是,List<Object> 和 List<?> 并不相同,你可以向 List<Object> 中插入 Object 对象,或者任何其子类对象,但是你只能向 List<?> 中插入 null 值。

3.下界通配符:? super 子类

如:<? super Integer>

假设你要编写一个将 Integer 对象放入列表的方法。为了最大限度地提高灵活性,希望该方法适用于 List<Integer>、List<Number>和 List<Object> 任何可以保存 Integer 值的东西。

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

类型擦除

我们要知道的一件事儿,编译器在编译时清除了所有类型参数,也就是说会将我们的类型参数进行实际的替换。

就算如此我们仍有使用泛型的理由

Java 语言中引入了泛型以在编译时提供更严格的类型检查并支持泛型编程。为了实现泛型,Java 编译器将类型擦除应用于:

类型擦除确保不会为参数化类型创建新类;因此,泛型不会产生运行时开销。

下面举两个例子

// 类型擦除前
public class Pair<K, V> {

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey(); { return key; }
    public V getValue(); { return value; }

    public void setKey(K key)     { this.key = key; }
    public void setValue(V value) { this.value = value; }

    private K key;
    private V value;
}
// 类型擦除后
public class Pair {

    public Pair(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    public Object getKey()   { return key; }
    public Object getValue() { return value; }

    public void setKey(Object key)     { this.key = key; }
    public void setValue(Object value) { this.value = value; }

    private Object key;
    private Object value;
}

// 类型擦除前
public static <T extends Comparable<T>> int findFirstGreaterThan(T[] at, T elem) {
    // ...
}
// 类型擦除后
public static int findFirstGreaterThan(Comparable[] at, Comparable elem) {
    // ...
}

最后感兴趣的同学可以参考:

Java Tutorial on Generics
Generics in the Java programming language

到此这篇关于Java 中泛型 T 和 ? 的区别详解的文章就介绍到这了,更多相关Java 泛型T和? 区别内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯