java 泛型

作者: zhbi98 | 来源:发表于2021-09-08 20:58 被阅读0次

    1. 泛型的优点

    在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

    使用泛型的例子:使用泛型指定数据类型和强制转换的比较,如下代码

    public class Generic<T> {
        private T t;
    
        public void set(T t) {
            this.t = t; 
        }
    
        public T get() {
            return t;
        }
    }
    
    
    public class GenericLearn {
        // 不指定类型
        public void noSpecifyType() {
            Generic generic = new Generic();
            generic.set("test");
    
            // 需要使用强制类型转换
            String test = (String) generic.get();
            System.out.println(test);
        }
    
        // 这里使用泛型指定类型,指定类型为String
        public void specifyType() {
            Generic<String> generic = new Generic<>();
            generic.set("test");
    
            // 不需要使用强制类型转换
            String test = generic.get();
            System.out.println(test);
        }
    
        public static void main(String[] args) {
    
        }
    }
    

    2. 使用泛型

    1. 在这里我们使用java 的可变长数组ArrayList作为例子说明泛型。

    使用ArrayList时,如果不定义泛型类型时,泛型类型实际上就是Object,此时,只能把<T>当作Object使用,没有发挥泛型的优势。

    // 编译器发生警告:
    List list = new ArrayList();
    list.add("Hello");
    list.add("World");
    String first = (String) list.get(0);
    String second = (String) list.get(1);
    

    当我们定义泛型类型<String>后,List<T>的泛型接口变为强类型List<String>。

    // 无编译器警告:
    List<String> list = new ArrayList<String>();
    list.add("Hello");
    list.add("World");
    // 无强制转型:
    String first = list.get(0);
    String second = list.get(1);
    

    当我们定义泛型类型<Number>后,List<T>的泛型接口变为强类型List<Number>。

    List<Number> list = new ArrayList<Number>();
    list.add(new Integer(123));
    list.add(new Double(12.34));
    Number first = list.get(0);
    Number second = list.get(1);
    

    2. 编译器如果能自动推断出泛型类型,就可以省略后面的泛型类型,甚至省略后面的泛型类型后尖括号也可以省略。例如,对于下面的代码:

    List<Number> list = new ArrayList<Number>();
    

    可以省略后面的泛型类型转化为

    // 这时需要编译器自己推断泛型类型
    List<Number> list = new ArrayList<>();
    
    // 甚至省略后面的泛型类型后尖括号也可以省略
    List<Number> list = new ArrayList();
    

    3. 在接口中使用泛型

    
    

    4. 编写泛型类

    // 定义类 Apple 时使用了泛型声明
    class Apple<T> {
        // 使用T类型形参定义实例属性
        private T info;
    
        public Apple() {
    
        }
    
        // 下面方法中使用T类型形参来定义构造函数
        public Apple(T info) {
            this.info = info;
        }
    
        public void setInfo(T info) {
            this.info = info;
        }
    
        public T getInfo() {
            return this.info;
        }
    }
    
    
    public class GenericLearn {
        public static void main(String[] args) {
            // 由于传给T形参的是String,所以构造器参数只能是String
            Apple<String> a1 = new Apple<>("苹果");
            System.out.println(a1.getInfo());
    
            // 由于传给T形参的是Double,所以构造器参数只能是Double或double
            Apple<Double> a2 = new Apple<>(100.06);
            System.out.println(a2.getInfo());
        }
    }
    ---------------------------------------------------------
    Apple
    100.06
    

    尖括号放置说明:

    20210428205844.jpg

    5. 泛型类中的静态方法使用泛型

    编写泛型类时,要特别注意,泛型类型<T>不能用于静态方法。无法在静态方法的方法参数和返回类型上使用泛型类型T。那我们该怎么办呢?我们可以这样对于静态方法,我们可以单独改写为“泛型”方法,只需要使用另一个类型即可。例如下面这样:

        // 编译时这将出现无法在静态上下文上引用非静态类型变量T的错误
        public static Apple<T>readObject(T a) {
            return new Apple<T>(a);
        }
    

    单独改写为“泛型”方法后的

        public static <K> Apple<K>readObject(K a) {
            return new Apple<K>(a);
        }
    

    具体实现:

    // 定义类 Apple 时使用了泛型声明
    class Apple<T> {
        // 使用T类型形参定义实例属性
        private T info;
    
        public Apple() {
    
        }
    
        // 下面方法中使用T类型形参来定义构造函数
        public Apple(T info) {
            this.info = info;
        }
    
        public void setInfo(T info) {
            this.info = info;
        }
    
        public T getInfo() {
            return this.info;
        }
    
        public static <K> Apple<K>readObject(K a) {
            return new Apple<K>(a);
        }
    }
    
    
    public class GenericLearn {
        public static void main(String[] args) {
            // 由于传给T形参的是String,所以构造器参数只能是String
            Apple<String> a1 = new Apple<>("Apple");
            System.out.println(a1.getInfo());
    
            // 由于传给T形参的是Double,所以构造器参数只能是Double或double
            Apple<Double> a2 = new Apple<>(100.06);
            System.out.println(a2.getInfo());
    
            Apple<Number> a3 = Apple.readObject(2021);
            System.out.println(a3.getInfo());
        }
    }
    ----------------------------------------------
    Apple
    100.06
    2021
    

    6. 支持多个泛型类型

    泛型还可以定义多种类型。例如,我们希望Apple不总是存储两个类型一样的对象,就可以使用类型<T, K>同时指定泛型参数类型,例如像下面这样:

    class Apple<T, K> {
        private T phone;
        private K series;
    
        public Apple(T phone, K series) {
            this.phone = phone;
            this.series = series;
        }
    
        public T readPhone() {
            return phone;
        }
    
        public K readNumber() {
            return series;
        }
    }
    
    
    public class GenericLearn {
        public static void main(String[] args) {
            // 使用的时候,需要指出两种类型 [String, Integer]
            Apple<String, Integer> a1 = new Apple<>("iphone", 12);
    
            System.out.println(a1.readPhone());
            System.out.println(a1.readNumber());
        }
    }
    -----------------------------------------------------------------
    iphone
    12
    

    相关文章

      网友评论

        本文标题:java 泛型

        本文链接:https://www.haomeiwen.com/subject/dgfhrltx.html