• 泛型


    1,Java 在 1.5 引入了泛型机制,泛型本质是参数化类型,也就是说变量的类型是一个参数,在使用时再指定为具体类型。泛型可以用于类、接口、方法,通过使用泛型可以使代码更简单、安全。

    2,泛型方法的编写,v和泛型v相同时候 的结果是不同的 

    泛型写在方法的时候   public static <K,V> void f(K k,V v) { ,如果通配符不在方法内有意思,可以不写在前面
       
    3,类型参数只存在于编译期,在运行时,Java 的虚拟机 ( JVM ) 并不知道泛型的存在。在 JVM 看来,保存的变量 a 还是 Object 类型。之所以取出来自动就是我们传入的参数类型,这是因为编译器在编译生成的字节码文件中插入了类型转换的代码,不需要我们手动转型了。

      2)任何在运行期需要知道确切类型的代码都无法工作,如new()

    4,了解数组的协变,一开始是上转型对象,new的子类,可以存子类和子类的子类,但无法形成多态(存他本身,和其他子类

    class Fruit {}
    class Apple extends Fruit {}
    class Jonathan extends Apple {}
    class Orange extends Fruit {}
    
    public class CovariantArrays {
        public static void main(String[] args) {       
            Fruit[] fruit = new Apple[10];
            fruit[0] = new Apple(); // OK
            fruit[1] = new Jonathan(); // OK
            // Runtime type is Apple[], not Fruit[] or Orange[]:
            try {
                // Compiler allows you to add Fruit:
                fruit[0] = new Fruit(); // ArrayStoreException
            } catch(Exception e) { System.out.println(e); }
            try {
                // Compiler allows you to add Oranges:
                fruit[0] = new Orange(); // ArrayStoreException
            } catch(Exception e) { System.out.println(e); }
            }
    } /* Output:
    java.lang.ArrayStoreException: Fruit
    java.lang.ArrayStoreException: Orange
    *///:~

    5,泛型同时也不支持协变,ArrayList<Fruit> flist = new ArrayList<Apple>();这个编译不过关

    6,上面问题可以用通配符解决,List<? extends Fruit> flist = new ArrayList<Apple>,不过我们也失去了向这个 List 添加任何对象的能力

    public boolean add(E e)
    public boolean contains(Object o)
    public int indexOf(Object o)

    因为add操作是用的泛型,在编译期不能判断这个变量的类型,而contains和indexof没有涉及到通配符,编译器允许使用这个方法

    因为extends有可能是任何一个子类,编译器认为是不安全的,若是<? super Fruit>的话,父类是fruit,可以添加fruit和fruit的子类(子类作为上转型对象),不过这个?的添加限制,在下面又不一样

    get方法得到的是父类和父类的基类,也可以强转型匹配的类

    public class Holder<T> {
        private T value;
        public Holder() {}
        public Holder(T val) { value = val; }
        public void set(T val) { value = val; }
        public T get() { return value; }
        public boolean equals(Object obj) {
        return value.equals(obj);
        }
        public static void main(String[] args) {
            Holder<Apple> Apple = new Holder<Apple>(new Apple());
            Apple d = Apple.get();
            Apple.set(d);
            // Holder<Fruit> Fruit = Apple; // Cannot upcast
            Holder<? extends Fruit> fruit = Apple; // OK
            Fruit p = fruit.get();
            d = (Apple)fruit.get(); // Returns ‘Object’
            try {
                Orange c = (Orange)fruit.get(); // No warning
            } catch(Exception e) { System.out.println(e); }
            // fruit.set(new Apple()); // Cannot call set()
            // fruit.set(new Fruit()); // Cannot call set()
            System.out.println(fruit.equals(d)); // OK
        }
    } /* Output: (Sample)
    java.lang.ClassCastException: Apple cannot be cast to Orange
    true
    *///:~

    7,下界<? super T>不影响往里存(T或者他的子类,因为虽然知道是Fruit的基类 但也不知道是往上数多少级的基类 只有Fruit以及它的派生类 编译器才能断定能触发多态),但往外取只能放在Object对象里,但还是强转,

    参考:https://segmentfault.com/a/1190000005337789

    知乎加强理解:https://www.zhihu.com/question/20400700/answer/117464182

    还有一句话:

    1) 参数写成:T<? super B>,对于这个泛型,?代表容器里的元素类型,由于只规定了元素必须是B的超类,导致元素没有明确统一的“根”(除了Object这个必然的根),所以这个泛型你其实无法使用它,对吧,除了把元素强制转成Object。所以,对把参数写成这样形态的函数,你函数体内,只能对这个泛型做插入操作,而无法读

    2) 参数写成: T<? extends B>,由于指定了B为所有元素的“根”,你任何时候都可以安全的用B来使用容器里的元素,但是插入有问题,由于供奉B为祖先的子树有很多,不同子树并不兼容,由于实参可能来自于任何一颗子树,所以你的插入很可能破坏函数实参,所以,对这种写法的形参,禁止做插入操作,只做读取

    8,例题:https://www.nowcoder.com/test/question/done?tid=17674447&qid=7693#summary

    9,泛型类和泛型方法的编写可以看看:https://blog.csdn.net/s10461/article/details/53941091

  • 相关阅读:
    gson和fastjson
    Hive和HBase的区别
    mac 比较两个文件
    mysql 查找在另一张表不存在的数据
    mysql 根据一张表更新另一张表
    shell调试
    目标
    百度在线面试总结
    20170109| javascript记录
    php-fpm问题
  • 原文地址:https://www.cnblogs.com/vhyc/p/9427979.html
Copyright © 2020-2023  润新知