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

  • 相关阅读:
    104.Maximum Depth of Binary Tree
    103.Binary Tree Zigzag Level Order Traversal
    102.Binary Tree Level Order Traversal
    101.Symmetric Tree
    100.Same Tree
    99.Recover Binary Search Tree
    98.Validate Binary Search Tree
    97.Interleaving String
    static静态初始化块
    serialVersionUID作用
  • 原文地址:https://www.cnblogs.com/feng9exe/p/9149763.html
Copyright © 2011-2022 走看看