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