• Java 泛型类型基础


    为什么要使用泛型?

    未使用泛型的情况:

    // 创建列表类
    List list = new ArrayList();
    // 添加一个类型为 String 的列表元素
    list.add("hello");
    // 强制转换为 String 类型,再赋值给类型为 s 的引用变量
    String s = (String) list.get(0);
    

    使用泛型的情况:

    // 创建泛型类,<String> 为类型参数
    List<String> list = new ArrayList<String>();
    // 添加一个类型为 String 的列表元素
    list.add("hello");
    // 这里不需要强制类型转换
    String s = list.get(0);
    

    好处:实现通用的泛型算法,处理不同类型的集合,可以自定义类型,类型安全,便于阅读。


    泛型类型

    一个泛型类型是一个类型参数化(<类型参数>)的泛型类或接口。

    一个简单的 Box 类

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

    Box 类中方法接受或返回一个对象,除了基本类型外你可以传入任何对象。编译时无法检查类是如何使用的,如果传入一个 Integer 并希望获取 Integer,但却错误传入 String 对象,那么就会导致运行错误。

    泛型版本 Box 类

    泛型类型定义格式:

    class name<T1, T2, ..., Tn> { /* ... */ } //T1, T2, ..., Tn为类型参数
    

    类型参数(也成为类型变量)跟在类名称后面,类型参数放在尖括号(<>)中(T1, T2, ..., and Tn),

    修改后的泛型 Box 类:

    /**
     * Box 类的泛型版本
     * @param <T> 类型的值被装箱
     */
    public class Box<T> {
        // T 表示 "类型"
        private T t;
    
        public void set(T t) { this.t = t; }
        public T get() { return t; }
    }
    

    上面代码中,Box 类中的所有 Object 类型都被替换为 T 类型,类型变量可以是指定的任何非基本类型:任何类类型、任何接口类型、任何数组类型,甚至是任何其他的类型变量。

    泛型技术也可以实现通用的泛型接口。

    类型参数的命名约定

    按照惯例,类型参数名为单个大写字母。这与已知的变量命名约定形成鲜明对比,这样做有充分的理由:如果没有这个约定,那么很难区分类型变量和普通类或接口名称之间的区别。

    最常用的类型参数名称:

    • E - Element (used extensively by the Java Collections Framework)
    • K - Key
    • N - Number
    • T - Type
    • V - Value
    • S,U,V etc. - 2nd, 3rd, 4th types

    调用和实例化泛型类型

    用具体的类型替换类型变量 T 就可以实例化泛型类型,例如:

    Box <Integer> integerBox = new Box <Integer>();
    

    通用类型的调用一般称为“参数化类型”。

    像往常一样使用new关键字实例化,但在类名和括号之间放置<Integer>:

    在 Java SE 7 和更高的版本中,只要编译器可以根据上下文确定或推断类型参数,就可以用一组空类型参数(<>)替换调用泛型类的构造函数所需的类型参数。例如:

    Box<Integer> integerBox = new Box<>();
    

    多类型参数

    泛型类可以拥有多个类型参数。例如,实现通用 Pair 接口的通用 OrderedPair 类:

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

    以下语句创建 OrderedPair 类的两个实例:

    Pair <String,Integer> p1 = new OrderedPair <String,Integer>(“Even”,8);
    Pair <String,String> p2 = new OrderedPair <String,String>(“hello”,“world”);
    

    新的OrderedPair <String,Integer>代码将 K 实例化为一个字符串,将 V 实例化为一个整数。因此,OrderedPair 的构造函数的参数类型分别是 String 和 Integer。由于自动装箱,将 String 和 int 传递给类是有效的。

    正如前文所述,由于Java编译器可以从OrderedPair <String,Integer>声明中推断 K 和 V 类型,因此可以使用以下缩写:

    OrderedPair <String,Integer> p1 = new OrderedPair <>(“Even”,8);
    OrderedPair <String,String> p2 = new OrderedPair <>(“hello”,“world”);
    

    参数化类型

    您也可以用参数化类型(即List )替换类型参数(即 K 或 V)。例如,使用OrderedPair <K,V>示例:

    OrderedPair <String,Box <Integer>> p = new OrderedPair <>(“primes”,new Box <Integer>(...));
    

    原始类型

    原始类型是没有任何类型参数的泛型类或接口的名称。

    例如,给定一个 Box 泛型类:

    public class Box<T> {
        public void set(T t) { /* ... */ }
        // ...
    }
    

    要创建参数化类型,为形式类型参数 T 提供实际类型参数:

    Box<Integer> intBox = new Box<>();
    

    如果省略实际类型参数,则会创建一个Box<T>的原始类型:

    Box rawBox = new Box();
    

    因此,Box 是泛型Box<T>的原始类型。但是,非泛型类或接口类型不是原始类型。

    在 JDK5.0 之前很多 API 类(如 Collections 类)不是通用的,为了向后兼容,允许将参数化类型分配给其原始类型:

    Box<String> stringBox = new Box<>();
    Box rawBox = stringBox;               // OK
    

    但是,将原始类型分配给参数化类型,编译器会发出一个警告:

    Box rawBox = new Box();           // rawBox 是 Box<T> 的原始类型
    Box<Integer> intBox = rawBox;     // warning: unchecked conversion
    

    如果使用原始类型来调用相应泛型类型中定义的泛型方法,则还会收到警告:

    Box<String> stringBox = new Box<>();
    Box rawBox = stringBox;
    rawBox.set(8);  // warning: unchecked invocation to set(T)
    

    该警告显示原始类型绕过了泛型类型检查,将不安全代码的捕获推迟到运行时。因此,你应该避免使用原始类型。

  • 相关阅读:
    PYTHON核心编程第7章 7.5题
    openssl大漏洞
    运维之路
    组件嵌套+Mixin函数demo
    React初识(按钮点击+输入绑定)
    vue切换按钮(关闭消失型)
    动态发表之后的显示时间
    展开全部的实现
    前端进行后台数据的处理
    将项目上传到git上,并在测试服务器上运行
  • 原文地址:https://www.cnblogs.com/nwgdk/p/8909248.html
Copyright © 2020-2023  润新知