zoukankan      html  css  js  c++  java
  • C++泛型 && Java泛型实现机制

    C++泛型 
      C++泛型跟虚函数的运行时多态机制不同,泛型支持的静态多态,当类型信息可得的时候,利用编译期多态能够获得最大的效率和灵活性。当具体的类型信息不可得,就必须诉诸运行期多态了,即虚函数支持的动态多态。

      对于C++泛型,每个实际类型都已被指明的泛型都会有独立的编码产生,也就是说list<int>list<string>生成的是不同的代码,编译程序会在此时确保类型安全性。由于知道对象确切的类型,所以编译器进行代码生成的时候就不用运用RTTI,这使得泛型效率跟手动编码一样高。 
      显然这样的做法增加了代码空间,相比运行时多态,是以空间换时间。

    Java泛型 
      当编译器对带有泛型的 Java 代码进行编译时,它会去执行类型检查和类型推断,然后生成普通的不带泛型的字节码,这种字节码可以被一般的 Java 虚拟机接收并执行,这种技术被称为擦除(erasure)。

      可见,编译器可以在对源程序(带有泛型的 Java 代码)进行编译时使用泛型类型信息保证类型安全,同时在生成的字节码当中,将这些类型信息清除掉。 
      如在代码中定义的List<object>List<String>等类型,在编译后都会编程List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。

    擦除的原则: 
      1)所有参数化容器类都被擦除成非参数化的(raw type);如List<E>、List<List<E>>都被擦除成List; 
      2)所有参数化数组都被擦除成非参数化的数组;如List<E>[],被擦除成List[]; 
      3)Raw type的容器类,被擦除成其自身,如List 被擦除成List; 
      4)原生类型(int,String还有wrapper类)都擦除成他们的自身; 
      5)参数类型E,被擦除成Object; 
      6)所有约束参数如<? Extends E>、<X extends E>都被擦除成E; 
      7)如果有多个约束,擦除成第一个,如<T extends Object & E>,则擦除成Object;

    来看个例子:

    public class Pair<T> { 
        public Pair(T first,T second) { 
            this.first = first; 
            this.second = second; 
        } 
        public T getFirst() { return first; } 
        public T getSecond() { return second; } 
        public void setFirst(T first) { this.first = first; } 
        public void setSecond(T second) { this.second = second; } 
        private T first; 
        private T second; 
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      擦除后变为:

    public class Pair { 
        public Pair(Object first,Object second) { 
            this.first = first; 
            this.second = second; 
        } 
        public Object getFirst() { return first; } 
        public Object getSecond() { return second; } 
        public void setFirst(Object first) { this.first = first; } 
        public void setSecond(Object second) { this.second = second; } 
        private Object first; 
        private Object second; 
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    public class QMI<T extends List> { 
        public Interval(T value) { 
            this.value = value;
        } 
        private T value; 
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      擦除后:

    public class QMI { 
        public Interval(List value) { 
            this.value = value;
        } 
        Private List value; 
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      那么,进行类型擦除后,在调用时怎么知道其真实类型,放心吧,编译器帮我们做好了一切,以后上面的Pair为例: 
       原代码为:

        Pair<String> pair = new Pair<>("", "");
        pair.setFirst("QMI");
        pair.setSecond("Kang");
        String first = pair.getFirst();
        String second = pair.getSecond();
    • 1
    • 2
    • 3
    • 4
    • 5

      反编译后为:

        Pair pair = new Pair("", "");
        pair.setFirst("QMI");
        pair.setSecond("Kang");
        String first = (String)pair.getFirst();
        String second = (String)pair.getSecond();
    • 1
    • 2
    • 3
    • 4
    • 5

      可以看到,编译器帮我们做了自动类型转换。

      对于泛型,我们可以利用Java单根继承特性实现类似效果,但是因为此时编译器并不做类型检查,这种检查是在运行时进行的,推迟了发现程序中错误的时间。

      而利用泛型机制,编译器承担了全部的类型检查工作,确保类型的安全性。以List<Object>List<String>为例来具体分析:

    public void test() {    
        List<String> list= new ArrayList<String>();    
        List.add(123); //编译错误 
    }  
    • 1
    • 2
    • 3
    • 4

      这里,声明为List的集合中却被添加了一个Integer类型的对象。这显然是违反类型安全的原则的,在某个时候肯定会抛出ClassCastException。因此,编译器禁止这样的行为。编译器会尽可能的检查可能存在的类型安全问题。对于确定是违反相关原则的地方,会给出编译错误。当编译器无法判断类型的使用是否正确的时候,会给出警告信息。此种机制有利于尽早地发现并改正错误。

      让我再来看一个问题:

    public class QmiV<T> {
        private T value;
    
        public T getValue() {
            return this.value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      擦除后:

    public class QmiV<Object> {
        private Object value;
    
    
        public Object getValue() {
            return this.value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //子类
    public class QmiVD extends QmiV<Person> {
    
        @Override
        public Person getValue() {
            return super.getValue();
        }
    
        @Override
        public void setValue(Person value) {
            super.setValue(value);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

      擦除后:

    public class QmiVD extends QmiV<Person> {
        public QmiVD() {
        }
    
        public String getValue() {
            return (Person)super.getValue();
        }
    
        public void setValue(Person value) {
            super.setValue(value);
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      可以看到,对于setValue方法,父类的类型是Object,而子类的类型是Person,参数类型不一样,所以这里实现的不是重写,而是重载。 
    实际中,是利用桥方法解决这个问题的。

      桥方法就是生成一个中间层,其参数类型都是Object,也就是说,子类中真正覆盖父类两个方法的就是两个我们看不到的桥方法。桥方法的内部再去调用我们自己重写的那两个方法。

    总结: 
      C++泛型和Java泛型非常类似,但是有着本质不同。 
      首先,Java 语言中的泛型不能接受基本类型作为类型参数――它只能接受引用类型。这意味着可以定义 List<Integer>,但是不可以定义 List<int>。 
      其次,在 C++ 模板中,编译器使用提供的类型参数来生成不同代码。而 Java 中的泛型,编译器仅仅对这些类型参数进行擦除和替换。类型 ArrayList<Integer> 和 ArrayList<String> 的对象共享相同的类,并且只存在一个 ArrayList 类。

    参考: 
    https://zh.wikipedia.org/zh-cn/%E6%B3%9B%E5%9E%8B 
    http://www.ibm.com/developerworks/cn/java/j-jtp01255.html 
    http://www.infoq.com/cn/articles/cf-java-generics

  • 相关阅读:
    写一个函数的程序,判断是否是浮点数
    写一个函数,输入一个数,随机生成N条邮箱
    day4-python基础知识 <元组&&集合>
    day4-python基础知识<文件操作>
    程序--用户登录--<while循环>
    程序--<猜数字小游戏>--for
    使用ajax后提交事件后禁用按钮,事件执行完毕后,重新启用按钮
    ajax在弹出对话框中实现一个UpdateProgress进度条控件源代码
    运算符的结合性
    匿名方法
  • 原文地址:https://www.cnblogs.com/feng9exe/p/9149763.html
Copyright © 2011-2022 走看看