泛型

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>();这个编译不过关

  但map<String, Object>支持

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

posted on 2018-08-06 00:00  Kooing  阅读(164)  评论(0)    收藏  举报

导航