zoukankan      html  css  js  c++  java
  • 什么是泛型?泛型的基本原理与使用优势。

    1. 什么是泛型?

    泛型将接口的概念进一步延伸,“泛型”的字面意思就是广泛的类型。类、接口和方法代码可以应用于非常广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码可以用于多种数据类型,这样不仅可以复用代码,降低耦合性,而且还提高了代码的可读性以及安全性。讲起来优点抽象,我们看个实际的例子。

    2. 先来看一个简单的泛型例子

    package genericity.demo;
    
    /**
     * @author BastetCat
     * @data 2019/8/8 21:14
     */
    
    public class Pair<T> {
        T one;
        T two;
    
        public Pair(T one, T two) {
            this.one = one;
            this.two = two;
        }
    
        public T getOne() {
            return one;
        }
    
        public T getTwo() {
            return two;
        }
    }
    

    观察和普通类的区别:

    1. 类名后面多了一个<T>
    2. one 和 two的类型都是T

    3. 那么T是什么呢?

    T 表示类型参数

    泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入。

    现在我们知道了,泛型把类型作为了参数来使用。

    4. 如何使用泛型,并将类型作为参数传入呢?

    如下代码:我们分别new了三Pair对象,其中传入了不同类型的类型参数(Integer、Character、String)。

    package genericity.demo;
    
    /**
     * @author BastetCat
     * @data 2019/8/8 21:22
     */
    
    public class Test {
        public static void main(String[] args) {
            
            Pair<Integer> pairInteger =  new Pair<Integer>(1,2);
            int one1 = pairInteger.getOne();
            int two1 = pairInteger.getTwo();
            System.out.println("one:"+one1+",two:"+two1);
    
            Pair<Character> pairCharacter  =  new Pair<Character>('一','二');
            char one2 = pairCharacter.getOne();
            char two2 = pairCharacter.getTwo();
            System.out.println("one:"+one2+",two:"+two2);
    
            Pair<String> pairString  =  new Pair<String>("I","II");
            String one3 = pairString.getOne();
            String two3 = pairString.getTwo();
            System.out.println("one:"+one3+",two:"+two3);
        }
    }
    

    结果如下:

    one:1,two:2
    one:一,two:二
    one:I,two:II
    

    当然我们不仅可以传入一个类型参数,也可以传入多个类型参数。多个类型参数之间用 逗号“,”隔开。如下面的例子:

    package genericity.demo;
    
    /**
     * @author BastetCat
     * @data 2019/8/8 21:37
     */
    
    public class PairTwo <U,V> {
        U one;
        V two;
    
        public PairTwo(U one, V two) {
            this.one = one;
            this.two = two;
        }
    
        public U getOne() {
            return one;
        }
    
        public V getTwo() {
            return two;
        }
    }
    

    可以这么使用:

    PairTwo<String,Integer> pairTwo = new PairTwo<>("牛牛",20);
    

    注意:自 Java 7 开始,支持省略后面的类型参数,让书写更简单些。

    5. 泛型的基本原理

    泛型类型参数到底是什么?为什么一定要定义类型参数呢?定义普通类,直接使用Object也是可以呀。如之前的Pair类我们可以写成:

    package genericity.demo;
    
    /**
     * @author BastetCat
     * @data 2019/8/8 21:44
     */
    
    public class PairObject {
        Object one;
        Object two;
    
        public PairObject(Object one, Object two) {
            this.one = one;
            this.two = two;
        }
    
        public Object getOne() {
            return one;
        }
    
        public Object getTwo() {
            return two;
        }
    }
    

    然后这样使用,也是同样的效果:

    package genericity.demo;
    
    /**
     * @author BastetCat
     * @data 2019/8/8 21:46
     */
    
    public class TestPairObject {
        public static void main(String[] args) {
            PairObject pairObject1 = new PairObject(1,2);
            int one1 =(int)pairObject1.getOne();
            int two1 =(int)pairObject1.getTwo();
            System.out.println("one:"+one1+",two:"+two1);
    
            PairObject pairObject2 = new PairObject("yi","er");
            String one2 =(String)pairObject2.getOne();
            String two2 =(String)pairObject2.getTwo();
            System.out.println("one:"+one2+",two:"+two2);
        }
    }
    
    

    输出结果:

    one:1,two:2
    one:yi,two:er
    

    我们可以看到,确确实实我们的使用Object + 强制类型转换也实现了相同的结果。事实上,我们Java泛型的内部原理就是这样的。

    我们使用JAD工具来反编译我们的Pair.class 与 Test.class得到的结果如下:

    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
    // Jad home page: http://www.kpdus.com/jad.html
    // Decompiler options: packimports(3) 
    // Source File Name:   Pair.java
    
    package genericity.demo;
    public class Pair
    {
        public Pair(Object obj, Object obj1)
        {
            one = obj;
            two = obj1;
        }
        public Object getOne()
        {
            return one;
        }
        public Object getTwo()
        {
            return two;
        }
        Object one;
        Object two;
    }
    
    
    
    
    // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
    // Jad home page: http://www.kpdus.com/jad.html
    // Decompiler options: packimports(3) 
    // Source File Name:   Test.java
    
    package genericity.demo;
    import java.io.PrintStream;
    // Referenced classes of package genericity.demo:
    //            Pair
    
    public class Test
    {
        public Test()
        {
        }
        public static void main(String args[])
        {
            Pair pair = new Pair(Integer.valueOf(1), Integer.valueOf(2));
            int i = ((Integer)pair.getOne()).intValue();
            int j = ((Integer)pair.getTwo()).intValue();
            System.out.println((new StringBuilder()).append("one:").append(i).append(",two:").append(j).toString());
            Pair pair1 = new Pair(Character.valueOf('u4E00'), Character.valueOf('u4E8C'));
            char c = ((Character)pair1.getOne()).charValue();
            char c1 = ((Character)pair1.getTwo()).charValue();
            System.out.println((new StringBuilder()).append("one:").append(c).append(",two:").append(c1).toString());
            Pair pair2 = new Pair("I", "II");
            String s = (String)pair2.getOne();
            String s1 = (String)pair2.getTwo();
            System.out.println((new StringBuilder()).append("one:").append(s).append(",two:").append(s1).toString());
        }
    }
    

    通过以上的分析:

    我们可以得知,Java的编译器将java源文件编译成字节码.class文件,虚拟机加载并运行。对于泛型类,java编译器会将其转换为普通的非泛型代码。将类型T擦除,然后替换为Object,插入必要的强制类型转换。Java虚拟机实际执行的时候,并不知道泛型这回事,只知道普通的类及代码。

    那么为什么泛型要这样设计呢?

    因为泛型是 Java 5 以后才支持的,这么设计是为了兼容性,而不得已的一个选择。

    6. 使用泛型的好处

    • 代码复用:我们一套代码可以支持不同的类性。
    • 降低了耦合性:代码逻辑和数据类型之间分离,实现了解耦。
    • 更好的可读性:我们在使用集合的时候,定义了一个list 如List<String>,一看便知道这个一个存放String类型的list。
    • 更高的安全性:语言和程序设计的一个重要目标就是将bug消灭在摇篮里,能在写的时候消灭,就不要留在运行的时候。如我们定义一个List<String>这样的一个list。当我们往list里面放其他非String类型的数据时,我们的IDE(如Eclipse)就会报错提示。就算没有IDE。编译时,Java编译器也会提示,这称之为类型安全。这样就为程序设置了一道安全防护。同样的,使用泛型还可以省去使用普通对象时繁琐的强制类型转换。相反,使用普通对象,编译时并不会提示。假如传入的参数类型和最后强制类型转换的类型不一致。运行时就会出现ClassCastException,使用泛型则不会。
  • 相关阅读:
    hadoop面试时的一些问题解答
    FTRL(Follow The Regularized Leader)学习总结
    循环神经(LSTM)网络学习总结
    深度学习中常用的激活函数
    TF.learn学习
    TensorFlow实现分布式计算
    TensorFlow TensorBoard使用
    深度学习总结
    Spark SQL相关总结
    推荐系统/广告系统索引目录
  • 原文地址:https://www.cnblogs.com/nm666/p/11324345.html
Copyright © 2011-2022 走看看