------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、泛型概念
泛型(Generic type 或者generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
限制泛型的可用类型
由于没有限制class GenericsFoo<T>类型持有者T的范围,实际上这里的限定类型相当于Object,这和“Object泛型”实质是一样的。限制比如我们要限制T为集合接口类型。只需要这么做:
class GenericsFoo<T extends Collection>,这样类中的泛型T只能是Collection接口的实现类,传入非Collection接口编译会出错。
注意:<T extends Collection>这里的限定使用关键字 extends,后面可以是类也可以是接口。但这里的extends已经不是继承的含义了,应该理解为T类型是实现Collection接口的类型,或者T是继承了XX类的类型。
下面我们根据一个例子来理解泛型:
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List list = new ArrayList();
list.add("qqyumidi");
list.add("corn");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // 1
System.out.println("name:" + name);
}
}
}
在如上的编码过程中,我们发现主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。
将上面的例子改为采用泛型来定义Listimport java.util.ArrayList;
import java.util.List;
public class <span style="font-family:SimSun;">Tes</span>t {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("qqyumidi");
list.add("corn");
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // 1
System.out.println("name:" + name);
}
}
}
采用泛型写法后,在//1处想加入一个Integer类型的对象时会出现编译错误,通过List<String>,直接限定了list集合中只能含有String类型的元素,从而在//2处无须进行强制类型转换,因为此时,集合能够记住元素的类型信息,编译器已经能够确认它是String类型了。
二、泛型的好处
①把运行时的异常转到了编译时期。
②避免了程序员强制类型的转换。
什么时候定义泛型?
当我们定义方法的时候,由于不知道调用者会传入什么样的参数,这时候我们就可以定义泛型,但是当调用者在使用方法的时候,肯定知道自己需要传入什么样类型的参数,所以当调用者调用的时候,就要传入具体的引用数据类型。(不传入泛型的时候,那默认的类型就是Object)
由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。例如:
<span style="font-family:SimSun;">import java.util.ArrayList;
public class tt {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(12);
list.getClass().getMethod("add", Object.class).invoke(list, "黑马程序员");
System.out.println(list);
}</span>
上述代码使用Java反射技术可以往泛型为Integer的ArrayList的对象中放入一个String对象。
三、类型通配符(?)
泛型的通配符: ?就是通配符
当我们定义类,方法(参数)不确定的时候,可以用?来表示。例如:
public void getId(<?> e){}
由于在方法中,传入的数据类型不确定,所以用到了泛型,但是在调用的时候,调用者肯定知道。如:
int i = 98;
getId(i);
String s ="98";
getId(s);
泛型就是要限制传递参数类型的,但是又可以接收任意的类型。
为了表示各种泛型的父类,Java使用"?"来表示泛型通配.即List<?>来表示各种泛型List的父类.带这种通配符List泛型不能设置(set)元素,只能获取(get)元素。因为程序无法确定List中的类型,所以不能添加对象。但获取的对象肯定是Object类型。
例如下面的代码编译时就会出错 List<?> list = new ArrayList<>();
list.add(new Object());
泛型的限定
限定通配符的上边界:
正确:List<? extends Number> v = new List<Integer>();
错误:List<? extends Number>v = new List<String>();
限定通配符的下边界:
正确:List<? super Integer>v =new List<Number>();
错误:List<? super Integer>v =new List<Byte>();
注意:限定通配符总是包括自己。
泛型上限
<? extends Collection>这被我们称之为上限,因为只能作用到其最顶层(父类),
通过这样的定义,可以把其子类当做参数来传递,这主要使用在添加的。 因为在取出的时候方便,不用再进行类型的判断。
泛型下限
<? super Comparable> 这就是下限,通常用来进行比较。
比较的时候既可以使用自己的比较器,也可以使用父类的比较器。
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------