泛型的优点
- 适用于多种数据类型执行相同的代码
- 类型安全,编译期对参数类型判断,增加安全性
- 取消强制类型转换
泛型分类
从List和ArrayList基本可以学习到泛型所有的知识点。泛型分为泛型类,泛型接口,泛型方法。先上一些源码:
public interface List<E> extends Collection<E> {
E get(int index);
void add(int index, E element);}
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
泛型接口
List是个接口对象,List 中E也就是所谓的泛型,我们都知道我们初始化List都会声明List,List等,而E也就是泛指所有类型。当然是指继承Object对象的所有类型,这也是为何List无法传入基本数据类型。
泛型类
ArrayList也就是泛型类,其实原理与接口类似。此处值得注意的是:ArrayList指定的泛型与父类AbstractList及List指定的泛型对象都是E,也就是必须为同一类型。当然可以设置多个泛型。可以参考下HashMap。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
泛型方法
可以参考ArrayList.toArray方法。
首先声明泛型,其次返回值为T的数组,传入值也为T的数组
进阶
不知道大家是否使用过MVP的框架,简单代码如下。
public class MVPBaseActivity<V extends BaseView, T extends BasePresenterImpl<V>>
extends Activity {
}
从代码中可以看出,我希望限制泛型为BaseView的字类型,则使用V extends BaseView。其中,extends不是类继承的extends,两者并无关系,BaseView可以为类,也可以为接口,类继承的extends 无法继承接口,正可以说明两者无关系。
通配符 ?
无边界通配符
先看例子,我们经常使用HashMap,初始化HashMap时,可能会有如下定义:
HashMap<String, ?> map = new HashMap<>();
当我们不确定hashMap中value的 类型时,我们可以采用通配符?代替具体类型。通配符?只能用来指定泛型,其他无法使用。
通配符的限定
extends
举例就比较好理解:
HashMap<String, ? extends Number> map = new HashMap<>();
限制通配符只能为Number的子类型
super
通过extends,可以理解super,也就是限制其为某个特定的父类。仍然举个例子,限制value为String的父类
HashMap<String, ? super String> map = new HashMap<>();
又一个很绕弯的理解:假设三个类Plant, Fruit,Apple三个类,依次继承关系。Fruit extends Plant, Apple extends Fruit.现有代码:
List<? super Fruit> list;
list = new ArrayList<Fruit>();
list = new ArrayList<Plant>();
list = new ArrayList<Apple>(); // 此行编译报错,比较好理解。因为通配符要求是Fruit的父类,而Apple不是Fruit的父类。
---------------------------------------------------------------------------
list = new ArrayList<Fruit>();
list.add(new Plant()); //此行编译报错
list.add(new Fruit());
list.add(new Apple());
明明要求是Fruit的父类,而add Plant的时候却会报错。原因:<? super Fruit>要求为Fruit的父类,也就是说是Fruit或者Plant,Apple是Fruit与Plant的子类,可添加,无疑。Fruit是Fruit和Plant的子类,故也可以添加。(此处子类是泛指)。而Plant不一定是Fruit的子类,故编译期无法识别,而显示报错!
泛型原理
泛型是JDK的一种语法糖,在JVM层面是没有泛型的。所以其实就是在编译期间,将泛型转为了实际使用的类,并且也会做类型检查,如果类型不符合要求,则会编译报错。在转成字节码时,会有checkcast,将泛型转为指定类型
public class Generic<T> {
private T object;
public Generic(T object) {
this.object = object;
}
private T getObject() {
return this.object;
}
public static void main(String[] args) {
Generic<String> generic = new Generic<>("generic paradigm");
System.out.println(generic.getObject().toString());
}
}
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #3 // class com/roc/java/common/Generic
3: dup
4: ldc #4 // String generic paradigm
6: invokespecial #5 // Method "<init>":(Ljava/lang/Object;)V
9: astore_1
10: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
//getObject时,会有checkcast,将泛型转为String
14: invokespecial #7 // Method getObject:()Ljava/lang/Object;
17: checkcast #8 // class java/lang/String
20: invokevirtual #9 // Method java/lang/String.toString:()Ljava/lang/String;
23: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
26: return