jdk1.5加入了泛型这一概念。
泛型:用于解决安全问题,是一个安全机制。
好处:
1.把原本的运行时会出现的类型转换异常(ClassCastException),转移到程序编译时期,方便程序员解决问题,让运行时期的问题减少,提高程序的安全性。
2.避免了强制类型转换的麻烦。
3.实现模板设计模式
通过一个例子了解泛型:
这个程序中向集合中添加数据,前三个添加的是字符串,最后一个添加的是整数。
编译时并不会出现问题,但是运行时会出现类型转换异常。
使用泛型:
使用泛型,把集合所存储的数据类型事先规定好。所以在程序中如果有其他类型的数据,编译就不会通过。
不过在实验中我发现,现在的java版本已经会自动把整数转换为字符串,所以如果使用的jdk版本高的话,在集合中存入整数可能不会出现问题。
但是通过另一中途径一定可以发现问题。
在这个程序中我直接存了一个对象到集合中,编译也不会出错,不过运行时出现了类型转换异常(java.lang.ClassCastException)。
这样,使用泛型的好处就一目了然了。程序员不注意可能会在存放字符串的集合中存入整数,编译时可能不会出现问题,一位程序可以用了就完事了,但是运行时会出现问题。如果存数据的动作由用户完成,程序员没有提供数据类型这个问题,用户在使用是出现了问题,那就麻烦了。
通过上面的列子可以看到泛型的作用。
那么,java中什么时候使用泛型呢?
通常在集合框架中很常见,因为集合是存放对象的,不想数组那样,在定义一个数组时就把数组的长度,类型都定义好了,集合都没有定义,所以使用泛型可以规定集合存放的数据类型。
通过查看Collection接口的API文档可以发现:
接口 Collection<E>
方法摘要 | |
---|---|
boolean | add(E e) 确保此 collection 包含指定的元素(可选操作)。 |
注意Collection后面的<E>就是使用泛型,而在add()方法中,参数也规定为(E e)。
到这里基本可以理解反省的使用方法了,拿集合来说:
<>是泛型的标志,<>里面的内容是要存入集合的数据的类型,把类型以参数的方式传递给集合类。
泛型在比较器中也用到。
java.util
接口 Comparator<T>
类型参数:
T
- 此 Comparator 可以比较的对象类型
方法摘要 | |
---|---|
int | compare(T o1, T o2) 比较用来排序的两个参数。 |
举个例子来看看 Comparator接口所使用到的泛型。
按照字符串的长度进行比较。
Comparator接口可以比较不同对象的同一属性,比如比较两个学生的年龄大小,但是不能比较一个学生的年龄和另一个学生的姓名的大小。
这也可以理解为什么Comparator接口要用到泛型。
自己也可以写泛型类,模仿集合接口使用泛型的方法即可。
泛型也称为模板或参数多肽,既是模板,就应该有模板的特性,下面通过自己我写的一个泛型类来体现泛型的模板特性。
通过程序可以看到,utils类为泛型类,在utils类中定义了show()方法,参数为T类型。
main()方法中既可以传递字符串参数给show()方法,也可以传递Integer参数给show()方法,这样就不用分别为字符串和整型参数写不同的show()方法,这就体现出了模板的特性。
而且当给泛型类传递了数据类型参数后,就规定了泛型类对象所能接受的参数类型,如果传递的参数类型为其他类型,则编译不会通过,而不用泛型,编译时可以通过的。
因为在确定了数据类型为String类型后,又传递了整型和Object类型的参数,所以编译不通过。
这就体现了泛型的安全性特点。
除了定义泛型类,还可以定义泛型方法,反省方法的操作性更灵活,因为定义泛型类的话,整个类里面的方法接收的参数类型都是一致的,而定义泛型方法,则方法可以接收不同类型的数据。
泛型方法的定义格式:
public <T> void show(T t)
{
System.out.println(t);
}
使用时给改方法传递任何类型的参数都可以。
那么,可以在泛型类中再定义泛型方法吗?
会出错吗?
哪个优先级更高?
猜是猜不出来的,试试就知道了。
定义泛型方法时,参数类型<T>应该放在返回类型符前面,修饰符后面。
public static <T> void show(T t){ } ;
再看下面一个例子:
Student类继承Person类,把Person对象和Student对象分别存入集合中,然后调用泛型方法printAll()打印。
如果使用反省方法打印的话,那么任意类型的对象都可以打印。
而如果只想让继承Person类的对象才可以使用打印方法打印,那些非Person继承类的对象不能打印,能否实现呢?答案是能。
这就要用到泛型限定。
语法是这样的:
ArrayList<? extends Person>
?是通配符,参数不是单单一个了,而是一个继承语句。
当给泛型方法printAll()传递非Person()子类对象作为参数是,编译就会报错,就不能使用该泛型方法。
这就完成了泛型限定,只有Person子类的对象才可以使用此泛型方法。
反省最重要的两个特点:
1. 安全
2. 模板
记住这两个特点,再去学习泛型就明白,所有泛型的使用都是为了实现这两个特性。