Generics
generic
generic /dʒəˈner.ɪk/
adjective 形容词1 FORMAL shared by, typical of or relating to a whole group of similar things, rather than to any particular thing:
共享,共性The new range of engines all had a generic problem with their fan blades.2 MAINLY US describes a product that is not marked with the name of the company that produced it:
一般的a generic drug
generic
adj 种属的;普遍的=general【记】源于:gene基因genus(n 生物学种类);genre(n 文学类型;流派)【区】genetic(adj 遗传的;起源的)"
反义词:
Specific
因为Object是所有对象的超类,所以它可以用在处理任意类型的时候作为占位。
然而,如果当该用String作为参数的时候,你使用了Object来占位,然后你可以将Object转为String来使用, 那么只有你才知道这个潜规则,Java程序是无法预知这个规则的,那么Java程序就无法帮你做必要的验证,当你不小心使用Integer作为参数的时候而想转换成String的时候就会出错,本质的原因是“信息丢失”了,你无法将这个潜规则反应到程序里面。
Generic的出现就是为了弥补这个缺陷!
type variable 类型变量
<E> -- 表示Element类型,即是对象的一个元素。
<K> -- 表示Key类型
<V> -- 表示Value类型
<T> -- 表示通用Generic类型
(这只是惯例而已,你可以任意定义类型变量名)
无泛型的代码如下:
Cell strCell = new Cell("Hello"); |
有泛型的代码如下:
Cell<String> strCell = new Cell<String>("Hello"); 你可以认为是没有泛型的代码少了点信息,也可以认为有泛型的代码多了点什么。 既然泛型的存在有一定的作用,那么势必需要多一点信息熵来给起作用的地方去参考。 换个角度来说,也是“数据”和“代码”的一次完美转换。本来我们需要重写任意多的代码,这条路走不通,因为违反了“有穷性”,我们无法完全定义出所有可能性的类型,即代码这条路走不通,于是我们改变了数据结构,在数据结构里面加了一个类型变量,这个变量来指代所有的可能性,以不变应万变。 |
1. 定义
- 泛型类不会因为类型参数不同而存在多份
- 泛型信息在编译后就被擦除,不存在于class文件中
- 泛型是给编译器提供信息而不是运行时
每一个对泛型类的调用都称为一次 parameterized invocation,即使同一个类的参数化的调用而产生的不同对象,但是本质上是共享同一份代码,是同一个类。
因此,对于类方法(Static)和类(Static)字段,都不可以使用泛型参数。
1.2. 类型参数的界限
<E>作为一般形式,并没有限制E的类型,也就是说E可以接受任意类型,但是现实需求往往是需要现在E在一定范围内
<E extends XXXClass>
1.3 子类型subtyping和通配符wildcards
wildcard '?'
List<? extends XXX> list
static double sum(List<Number> list) { double sum = 0.0; for (Number n : list) sum += n.doubleValue(); return sum;}List<Integer> l = new ArrayList<Integer>();l.add(1);l.add(4);l.add(9);double sum = sum(l); // INVALID: won't compile
11.5 背后实现原理: 编译时擦除
erasure:因为编译器基本上会将class文件里的所有的泛型类型信息擦除。
- 首先,如果是<E>就替换为Object
- 如果是< E extends ?>, 就替换为最近的那个?
- 如果使用到绑定的非Object方法,就做一个cast
类型擦除影响到如下2个关键点:
- 包含泛型类型的运行时操作
- 方法的重载(同类)和重写{继承}
擦除对运行时的影响如下:
- 无法 new T(), new T[n]
- =============================== 看不懂
对重载和重写的影响
class Base<T> { void m(int x) {} void m(T t) {} void m(String s) {} <N extends Number> void m(N n) {} void m(SingleLinkQueue<?> q) {}} 类型擦除后就是如下代码: |
void m(int x) {}void m(Object t) {}void m(String s) {}void m(Number n) {}void m(SingleLinkQueue q) {} |
泛型:
本质上参数化类型,主要是针对集合,因为如果是非集合,那么类型肯定已经确定了!!!
泛型可以用在类,接口和方法的创建中。
下面举一个用在泛型类中的例子,因为类生产的对象可以看出是一个字典,而字典就是一个集合!
package com.iu.www.generics;//泛型的写法,非常的通用,不需要类型转换public class Gen<T> { private T ob; public Gen(T ob){ this.ob = ob; } public T getOb() { return ob; } public void setOb(T ob) { this.ob = ob; } public void showType(){ System.out.println("the param is : "+ob.getClass().getName()); } public static void main(String[] args) { Gen<String> hello = new Gen<String>("Hello"); hello.showType(); }// 一般的写法, 因为Object没有泛化,所以不清楚Object是什么,所以有强制类型转换的过程,而且这个过程编译器没办法帮助检查,可能会有运行时错误public class Gen2 { private Object ob; //定义一个通用类型成员 public Gen2(Object ob) { this.ob = ob; } public Object getOb() { return ob; } public void setOb(Object ob) { this.ob = ob; } public void showTyep() { System.out.println("T的实际类型是: " + ob.getClass().getName()); } }// 最搓的写法, 每个类型都要重复写一遍,重复的模板代码 class GenString { private String ob; public GenString(String ob){ this.ob = ob; } public String getOb() { return ob; } public void setOb(String ob) { this.ob = ob; } public void showType(){ System.out.println("the param is : "+ob.getClass().getName()); } } class GenInteger { private Integer ob; public GenInteger(int ob){ this.ob = ob; } public Integer getOb() { return ob; } public void setOb(int ob) { this.ob = ob; } public void showType(){ System.out.println("the param is : "+ob.getClass().getName()); } }}
其中泛型的参数类型一般有如下几种,但是这只是一个建议而已,你可以改成任何你想要的名字。
E for an element type, K for a key type, V for a value type, T for a general type, and so forth.
注意如下代码,泛型定义的类型参数不一定是给构造器使用,它可以用在类的任何方法上。
package com.iu.www.generics;public class Map2<K,V> { public V get(K key) { return null; } public V put(K key, V value) { return null; } public static void main(String[] args) { Map2<String, Integer> stringIntegerMap2 = new Map2<String, Integer>(); stringIntegerMap2.put("",1); Integer integer = stringIntegerMap2.get(""); }}