一、需要泛型的原因:
List list = new ArrayList();
list.add("shiyanlou");
list.add("admin");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); // 1
System.out.println("name:" + name);
}
在上述代码中,定义了一个List类型的集合,先向其中加入了两个字符串类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。在之后的循环中,由于忘记了之前在list中也加入了Integer类型的值或其他编码原因,很容易出现类似于//1中的错误。因为编译阶段正常,而运行时会出
现“java.lang.ClassCastException”异常。因此,导致此类错误编码过程中不易发现。
在如上的编码过程中,我们发现主要存在两个问题:
1.当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
2.因此,//1处取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。
没有泛型操作集合的缺点:《1》因为
集合中存储都是以 Object 对象进行存储的
,所以从集合取出对象时,需要执行类型转换操作。《2》由于没有类型检查,可以向集合添加任意对象,不便于我们对集合的管理,有时候甚至会导致严重的错误。
而泛型帮我们提供了类型参数,让我们提前规定好集合对象的类型。
二、泛型
1、泛型:即“参数化类型”,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型定义为参数形式(可以称之为 类型形参),然后在使用/调用时传入具体的类型(类型实参)。
2、
//注意:引入了泛型以后,List<Object>, List<String>是两种不同的类型
ArrayList<String> files = new ArrayList<String>();
/*
*下面的代码是非法的
*List<Object> lo = files;
*/
在集合的类型后面添加上一对<>,尖括号中填上我们需要规范的集合里对象的类型,我们可以指定一个或多个类型参数的名字,同时也可以对类型参数的取值范围进行限定,多个类型参数之间用,分隔。这样编辑器便会在编译时帮我们检查出不符合规范的元素添加。
3、泛型的规则:
(1)泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
(2) 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
(3)泛型的类型参数可以有多个。
(4)泛型的参数类型可以使用 extends 语句。习惯上称为“有界类型”。
(5)泛型的参数类型还可以是通配符类型。例如 Class<?> classType = Class.forName("java.lang.String");
4、定义带类型参数的类
package com.shiyanlou;
/*
使用T代表类型,无论何时都没有比这更具体的类型来区分它。如果有多个类型参数,我们可能使用字母表中T的临近的字母,比如S。
*/
class Test
<T> {
private T ob;
/*
定义泛型成员变量,定义完类型参数后,可以在定义位置之后的方法的任意地方使用类型参数,就像使用普通的类型一样。
注意,父类定义的类型参数不能被子类继承。
*/
//构造函数
public Test(T ob) {
this.ob = ob;
}
//getter 方法
public T getOb() {
return ob;
}
//setter 方法
public void setOb(T ob) {
this.ob = ob;
}
public void showType() {
System.out.println("T的实际类型是: " + ob.getClass().getName());
}
}
public class TestDemo {
public static void main(String[] args) {
// 定义泛型类 Test 的一个Integer版本
Test
<Integer> intOb = new Test<Integer>(88);
intOb.showType();
int i = intOb.getOb();
System.out.println("value= " + i);
System.out.println("----------------------------------");
// 定义泛型类Test的一个String版本
Test<String> strOb = new Test<String>("Hello Gen!");
strOb.showType();
String s = strOb.getOb();
System.out.println("value= " + s);
}
}
运行结果:
5、使用泛型方法
《1》
public class Animal {
public Animal(){
System.out.println("我是动物");
}
}
public class Dog extends Animal {
public Dog(){
System.out.println("我是狗");
}
}
public class Test {
/*
注意:定义带类型参数的方法,其主要目的是为了表达多个参数以及返回值之间的关系。例如本例子中T和S的继承关系, 返回值的类型和第一个类型参数的值相同。
*/
public
<T, S extends T> T testDemo(T t, S s){
System.out.println("我是 T 类型,我的类型是" + t.getClass().getName());
System.out.println("我是 S 类型,我的类型是" + s.getClass().getName());
return t;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Test test = new Test();
Dog d = new Dog();
Animal a0 = new Animal();
Animal a1 = test.testDemo(a0, d);
System.out.println("我是整数 a,我的类型是" + a1.getClass().getName());
}
}
运行结果:
《2》上面的例子中我们对类型参数赋予了具体的类型,当然我们有时候也无法确定类型参数的类型,这个时候我们便可以使用通配符。如果仅仅是想实现多态,请优先使用通配符解决。
import java.util.List;
public class Test {
public void testDemo(
List<?> s){
for(Object obj:s){
System.out.println("我的类型是" + obj.getClass().getName());
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Test test = new Test();
Dog a0 = new Dog();
Animal a1 = new Animal();
List<Animal> s = new ArrayList<Animal>();
s.add(a0);
s.add(a1);
test.testDemo(s);
}
}
运行结果:
参考:
https://www.shiyanlou.com/courses/running
http://www.cnblogs.com/lwbqqyumidi/p/3837629.html