对于java的泛型我一直属于只知其一;不知其二的,日常真心用的很少。直到阅读《Effect Java》,看到不少日常不了解的用法,才下定决心,须要系统的学习,而且记录下来。java
根据《Java编程思想》中的描述,泛型出现的动机:git
有不少缘由促成了泛型的出现,而最引人注意的一个缘由,就是为了建立容器类。
复制代码
泛型的思想很早就存在,如C++中的模板(Templates)。模板的精神:参数化类型github
JDK 1.5时增长了泛型,在很大的程度上方便在集合上的使用。编程
public static void main(String[] args) {
List list = new ArrayList();
list.add(11);
list.add("ssss");
for (int i = 0; i < list.size(); i++) {
System.out.println((String)list.get(i));
}
}
复制代码
由于list类型是Object。因此int,String类型的数据都是能够放入的,也是均可以取出的。可是上述的代码,运行的时候就会抛出类型转化异常,这个相信你们都能明白。数组
public static void main(String[] args) {
List<String> list = new ArrayList();
list.add("hahah");
list.add("ssss");
for (int i = 0; i < list.size(); i++) {
System.out.println((String)list.get(i));
}
}
复制代码
在上述的实例中,咱们只能添加String类型的数据,不然编译器会报错。bash
泛型的三种使用方式:泛型类,泛型方法,泛型接口app
public class 类名 <泛型类型1,...> {
}
复制代码
public <泛型类型> 返回类型 方法名(泛型类型 变量名) {
}
复制代码
class Demo{
public <T> T fun(T t){ // 能够接收任意类型的数据
return t ; // 直接把参数返回
}
};
public class GenericsDemo26{
public static void main(String args[]){
Demo d = new Demo() ; // 实例化Demo对象
String str = d.fun("汤姆") ; // 传递字符串
int i = d.fun(30) ; // 传递数字,自动装箱
System.out.println(str) ; // 输出内容
System.out.println(i) ; // 输出内容
}
};
复制代码
public interface 接口名<泛型类型> {
}
复制代码
/**
* 泛型接口的定义格式: 修饰符 interface 接口名<数据类型> {}
*/
public interface Inter<T> {
public abstract void show(T t) ;
}
/**
* 子类是泛型类
*/
public class InterImpl<E> implements Inter<E> {
@Override
public void show(E t) {
System.out.println(t);
}
}
Inter<String> inter = new InterImpl<String>() ;
inter.show("hello") ;
复制代码
//定义接口时指定了一个类型形参,该形参名为E
public interface List<E> extends Collection<E> {
//在该接口里,E能够做为类型使用
public E get(int index) {}
public void add(E e) {}
}
//定义类时指定了一个类型形参,该形参名为E
public class ArrayList<E> extends AbstractList<E> implements List<E> {
//在该类里,E能够做为类型使用
public void set(E e) {
.......................
}
}
复制代码
父类派生子类的时候不能在包含类型形参,须要传入具体的类型ide
public class A extends Container<K, V> {}
学习
public class A extends Container<Integer, String> {}
测试
public class A extends Container {}
public class Person {
public <T> Person(T t) {
System.out.println(t);
}
}
复制代码
使用:
public static void main(String[] args) {
new Person(22);// 隐式
new <String> Person("hello");//显示
}
复制代码
特殊说明:
public class Person<E> {
public <T> Person(T t) {
System.out.println(t);
}
}
复制代码
正确用法:
public static void main(String[] args) {
Person<String> person = new Person("sss");
}
复制代码
PS:编译器会提醒你怎么作的
上界通配符顾名思义,<? extends T>表示的是类型的上界【包含自身】,所以通配的参数化类型多是T或T的子类。
它表示集合中的全部元素都是Animal类型或者其子类
List<? extends Animal>
复制代码
这就是所谓的上限通配符,使用关键字extends来实现,实例化时,指定类型实参只能是extends后类型的子类或其自己。
//Cat是其子类
List<? extends Animal> list = new ArrayList<Cat>();
复制代码
下界通配符<? super T>表示的是参数化类型是T的超类型(包含自身),层层至上,直至Object
它表示集合中的全部元素都是Cat类型或者其父类
List <? super Cat>
复制代码
这就是所谓的下限通配符,使用关键字super来实现,实例化时,指定类型实参只能是extends后类型的子类或其自己
//Animal是其父类
List<? super Cat> list = new ArrayList<Animal>();
复制代码
编译器编译带类型说明的集合时会去掉类型信息
public class GenericTest {
public static void main(String[] args) {
new GenericTest().testType();
}
public void testType(){
ArrayList<Integer> collection1 = new ArrayList<Integer>();
ArrayList<String> collection2= new ArrayList<String>();
System.out.println(collection1.getClass()==collection2.getClass());
//二者class类型同样,即字节码一致
System.out.println(collection2.getClass().getName());
//class均为java.util.ArrayList,并没有实际类型参数信息
}
}
复制代码
true
java.util.ArrayList
复制代码
public class GenericTest {
public static void main(String[] args) throws Exception {
getParamType();
}
/*利用反射获取方法参数的实际参数类型*/
public static void getParamType() throws NoSuchMethodException{
Method method = GenericTest.class.getMethod("applyMap",Map.class);
//获取方法的泛型参数的类型
Type[] types = method.getGenericParameterTypes();
System.out.println(types[0]);
//参数化的类型
ParameterizedType pType = (ParameterizedType)types[0];
//原始类型
System.out.println(pType.getRawType());
//实际类型参数
System.out.println(pType.getActualTypeArguments()[0]);
System.out.println(pType.getActualTypeArguments()[1]);
}
/*供测试参数类型的方法*/
public static void applyMap(Map<Integer,String> map){
}
}
复制代码
java.util.Map<java.lang.Integer, java.lang.String>
interface java.util.Map
class java.lang.Integer
class java.lang.String
复制代码
public static void main(String[] args) throws Exception {
//定义一个包含int的链表
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(2);
//获取链表的add方法,注意这里是Object.class,若是写int.class会抛出NoSuchMethodException异常
Method m = al.getClass().getMethod("add", Object.class);
//调用反射中的add方法加入一个string类型的元素,由于add方法的实际参数是Object
m.invoke(al, "hello");
System.out.println(al.get(2));
}
复制代码
public class User<K, V> {
public void show(K k) { // 报错信息:'show(K)' clashes with 'show(V)'; both methods have same erasure
}
public void show(V t) {
}
}
复制代码
因为泛型擦除,两者本质上都是Obejct类型。方法是同样的,因此编译器会报错。
换一个方式:
public class User<K, V> {
public void show(String k) {
}
public void show(V t) {
}
}
复制代码
使用结果:
编译器也不知道该建立那种类型的对象
public class User<K, V> {
private K key = new K(); // 报错:Type parameter 'K' cannot be instantiated directly
}
复制代码
静态方法没法访问类上定义的泛型;若是静态方法操做的类型不肯定,必需要将泛型定义在方法上。
若是静态方法要使用泛型的话,必须将静态方法定义成泛型方法。
public class User<T> {
//错误
private static T t;
//错误
public static T getT() {
return t;
}
//正确
public static <K> void test(K k) {
}
}
复制代码
public class User<T> {
private T[] values;
public User(T[] values) {
//错误,不能实例化元素类型为类型参数的数组
this.values = new T[5];
//正确,能够将values 指向类型兼容的数组的引用
this.values = values;
}
}
复制代码
泛型类不能扩展 Throwable,意味着不能建立泛型异常类 答案连接