泛型
泛型也叫参数化类型,对具体类型的使用起到辅助作用。Java 1.5之前是没有泛型的,以前从集合中读取每个对象都必须先进行转换,如果不小心存入集合中对象类型是错的,运行过程中转换处理会报错。有了泛型之后编译器会自动帮助转换,使程序更加安全,但是要正确使用泛型才能取得事半功倍的效果。
泛型类
public class CUstom<T> {//泛型写在类名后面
...
}
此时的 T 可以作为一个类型在整个类中使用,相当于Object类型。Object是所有类的超类(类的统称),语义不是很明确,因此会选择使用26个英语字母来代替。在使用的时候只需要将对应含义的英语单词的首字母来作为类型即可,对于常见的泛型模式,推荐的名称是:
K ——键,比如映射的键。
V ——值,比如 List 和 Set 的内容,或者 Map 中的值。
E ——异常类。
T ——泛型
/*T和U在类中就有了一个类型的作用*/
class Person<T,U> {//可同时写多个泛型
private T name;
private U age;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public U getAge() {
return age;
}
public void setAge(U age) {
this.age = age;
}
}
在创建泛型类的实例时,可以把尖括号里的泛型具体为某个类型
public static void main(String[] args) {
//类名后加泛型,右边类名后面的泛型在1.7之前需要显示指明类型,1.8之后可以不写
//泛型不能是基本数据类型,可以用其包装类
Person<String,Integer> cu = new Person<>();
cu.setName("李四");
cu.setAge(14);
//不把类的T泛型具体指定为String的话,默认得到的是Object类,这里就需要强制类型转换
String name = cu.getName();
int age = cu.getAge();
System.out.println(name+"的年龄是"+age);
}
/*程序运行结果
李四的年龄是14
*/
ArrayList类也是泛型类:
public class ArrayList<E> {
...
}
集合类泛型
不使用泛型的集合会出现的问题:
public static void main(String[] args) {
List list = new ArrayList();//没有使用泛型的集合
list.add("hahah");
list.add(4562);
list.add("apple");
Iterator it = list.iterator();//一个迭代器用于输出list集合
while(it.hasNext()) {
//当只需要输出字符串类型时,编译提示需要类型转换
String str = (String)it.next();
System.out.println(str+"--"+str.length());//输出字符串和该长度
}
}
编译时没报错,但在运行时会出现类型转换异常,如下:
hahah--5
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at cn.study.fx.Test.main(Test.java:21)
为了保证程序在运行状态下不出现问题,可以在输出字符串前使用instanceof进行判断,只输出字符串类型的元素。但java1.5有了泛型之后,使用泛型会更有效率,程序也更加安全。
集合使用泛型的好处:安全机制,将异常从运行时提前到了编译时,也不需要进行强制类型转换
集合类泛型代码演示:
//泛型格式(在尖括号内指明数据类型): <数据类型>
public static void main(String[] args) {
//类名后加泛型,右边类名后面的泛型在1.7之前需要显示指明类型,1.8之后可以不写
List<String> list = new ArrayList<String>();
list.add("hahah");
//泛型明确指明了集合中只能有String类型,该语句在编译时就会报错,而不是运行时出异常
list.add(4562);
list.add("apple");
//迭代器使用泛型,在使用元素时编译器会自动转换类型
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String str = it.next();//自动转换类型,不用再加 (String)
System.out.println(str+"--"+str.length());
}
}
/*运行结果
hahah--5
apple--5
*/
泛型的使用主要用在集合中,不过泛型也可以使用在类、接口和方法中。
泛型方法
前面的泛型类中可以看到,类中的方法或作为参数或作为返回值,已经使用了类的泛型,但并不是方法本身的泛型,那样的方法也不能称为泛型方法。
泛型方法代码格式:
public <A> A setName(A a) {//泛型声明在修饰符和返回值之间
A aa = a;//声明在方法上的泛型仅在在此方法内可以用
return aa;
}
注意:静态方法无法访问类上定义的泛型,如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
泛型方法与可变参数:
// “T... args”: 可变参数,可理解成任意个数的T类型参数,组成了一个T类型数组,数组名是args
public static <T> void printMsg( T... args){
//遍历输出传入的参数
for(T t : args){
System.out.println("泛型测试: " + t);
}
}
//测试
public static void main(String[] args) {
Person<String,Integer> pe = new Person<>();
printMsg(123,"ss",pe);
}
/*程序运行结果
泛型测试: 123
泛型测试: ss
泛型测试: cn.study.test.Person@39ed3c8d
*/