集合(8):泛型类、泛型方法、泛型接口
前言案例
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo1 {
public static void main(String[] args) {
//创建List集合对象
ArrayList list = new ArrayList();
//向集合中添加元素
list.add("hello");
list.add(10);//相当于向上转型,10 -- int -- Integer
list.add("world");
//获取迭代器对象
Iterator iterator = list.iterator();
//获取迭代器对象
while(iterator.hasNext()){
Object next = iterator.next();
String s = (String)next;
System.out.println(s);
}
}
}
执行结果如下:
我们按照正常的写法,在集合中添加一些不是同类型的数据,在遍历的时候向下转型报错了
ClassCastException:类型转换异常
为什么呢?
因为我们在存储数据的时候,存储了一些String和Integer类型的数据,但是呢,
我们遍历的时候,默认集合中只存放String类型的数据;但是在存储数据的时候
没有告诉我们只能存String类型的数据,如果能在存储的时候告诉我能存储哪些数据类型就好了
String[] arr = new String[3];//创建数组的时候明确的元素的类型
arr[0] = "hello";
// arr[1] = 20;当添加int类型的时候是不可行的
java中集合就模仿着数组呢,也有这样的做法,在创建集合的时候,就明确了元素的数据类型,
创建后,再往集合中加入的元素,只能是定义好的数据类型相关的数据了,然后再向下转型就没有问题了。
这样的技术,java中叫做:泛型
泛型
一、泛型的使用介绍
1、泛型的概述
把明确数据类型的工作,提前到了编译时期,在创建集合的时候明确数据类型,
这样的做法有点像把数据类型当作参数一样进行传递。
所以泛型还有一个名字叫做:参数化类型
2、泛型的定义格式
定义格式:
<引用数据类型>,在API中是<E>
注意:尖括号中的数据类型只能是引用数据类型,<E>在创建对象的时候添加在类后面
格式案例:
ArrayList<String> list = new ArrayList<String>();
JDK1.7之后会自动进行类型推断
所以后面的<String>中的String可加可不加,建议加上
当创建对象的时候加入了泛型,迭代器也要加上泛型
//获取迭代器对象
Iterator<String> iterator = list.iterator();
在没使用泛型的时候,添加不同类型元素的时候,不会报错,在向下转型的时候才会报错;
当使用泛型的时候,在添加元素的时候,编写代码的时候就会报错,提前报错,便于我们更改
3、泛型的好处
添加泛型之后
(1)将我们之前运行时候出现的问题,提前到了编译时期
(2)不需要强制类型转换了,而且还可以调用元素类型的方法
(3)优化了代码,消除不必要的黄色警告线
4、泛型的使用场景
通过观察API发现,泛型可以出现了类,接口,方法上,看到一些类似与<E>,
一般来说泛型出现在大多使用集合中
二、泛型的应用
1、泛型类
泛型类:把泛型定义在类上(在类名后面添加<变量名>)
格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
这里的<>里面的内容仅仅表示的是一种参数数据类型,参数类型是一种变量,
既然是一种变量,就符合变量的命名规则,可以是任意符合标识符起名规则的名字
泛型类的使用
//第1步:定义一个泛型类
class GenericTool1<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
//第2步:泛型类的测试
public class GenericTest1 {
public static void main(String[] args) {
/*
如果前面定义的类不加泛型,默认则是Object类型
GenericTool1 gt1 = new GenericTool1();
添加元素的时候,类型可以是任意的
gt1.setObj("hello");
gt1.setObj(20);
gt1.setObj(12.34);
gt1.setObj(new Student());
*/
//创建泛型类的对象
GenericTool1<String> gt = new GenericTool1<>();
//调用setObj()方法添加元素
gt.setObj("hello");
//gt2.setObj(20);定义过泛型之后,就不能再添加不同类型的元素
//调用getObj()方法获取元素
String obj = gt.getObj();
System.out.println(obj);
}
}
执行结果如下:
hello
Process finished with exit code 0
2、泛型方法
泛型方法:把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
例:
public<T> void show(T t){}
(1)在没使用泛型的时候,正常写个类来定义成员方法
//定义一个类
public class GenericTool2 {
public void show(String s){
System.out.println(s);
}
public void show(int i){
System.out.println(i);
}
public void show(double d){
System.out.println(d);
}
}
//定义一个测试类
public class GenericTest2 {
public static void main(String[] args) {
//创建类的对象
GenericTool2 gt1 = new GenericTool2();
//调取类中的show方法,并赋值
gt1.show(10);
gt1.show("hello");
gt1.show(12.34);
}
}
执行结果如下:
10
hello
12.34
Process finished with exit code 0
当我们不使用泛型的时候,在测试类中赋值的时候,必须要根据定义的成员方法参数类型来赋值,
当需要赋值很多类型的时候,需要定义的成员方法也很多,非常的麻烦。
(2)当使用泛型类的时候,泛型类的成员方法参数类型和泛型类一致
赋值很多类型的时候,就不需要定义很多发成员方法了
//定义一个泛型类
class GenericTool2<T> {
//定义成员方法,参数为T类型
public void show(T t){
System.out.println(t);
}
}
//定义一个测试类
public class GenericTest2 {
public static void main(String[] args) {
GenericTool2 gt1 = new GenericTool2();
//前面定义了泛型类,此处再赋值就可以随便赋值不同的类型了
gt1.show(10);
gt1.show("hello");
gt1.show(12.34);
}
}
执行结果如下:
10
hello
12.34
Process finished with exit code 0
(3)当定义了泛型类,泛型类的成员方法参数类型和泛型类一致,
如果在测试了中创建对象的时候也加上了泛型,会发生什么变化?
//定义一个泛型类
class GenericTool2<T> {
//定义泛型成员方法
public void show(T t){
System.out.println(t);
}
}
//定义一个测试类
public class GenericTest2 {
public static void main(String[] args) {
//创建泛型类对象的时候加上泛型
GenericTool2<String> gt2 = new GenericTool2<>();
gt2.show("hello");
//gt2.show(20);当创建对象的时候加上了泛型,那么赋值int类型就会报错
//想要赋值int类型的元素,就必须再创建一个int泛型的对象
GenericTool2<Integer> gt3 = new GenericTool2<>();
gt3.show(20);
}
}
执行结果如下:
hello
20
Process finished with exit code 0
由此可见,当定义了泛型类,泛型类的成员方法参数类型和泛型类一致,
在测试了中创建对象的时候也加上了泛型,
赋值不同类型的时候,需要创建很多的对象,就非常的麻烦
如果定义的类上面没有泛型的话,方法还能不能随便传参呢?
答案是可以的,不过这时候就需要用到泛型方法了
而且测试类中创建对象的时候不能有泛型了,不然会报错
//定义一个类
public class GenericTool2 {
//定义泛型成员方法
public<T> void show(T t){
System.out.println(t);
}
}
//定义一个测试类
public class GenericTest2 {
public static void main(String[] args) {
//创建对象
GenericTool2 gt = new GenericTool2();
//定义泛型方法以后,赋值的时候,可以传入任意类型的参数
gt.show("hello");
gt.show(20);
gt.show(true);
}
}
执行结果如下:
hello
20
true
Process finished with exit code 0
3、泛型接口
泛型接口:把泛型定义在接口上
格式:public interface 接口名<泛型类型1...>
案例:
//定义泛型接口,接口中的方法参数和接口一致
public interface GenericTool3<T> {
public abstract void show(T t);
}
//定义一个类来实现泛型接口,实现接口的类也要加上泛型
public class GenericTool3Impl<T> implements GenericTool3<T>{
//重写接口中的抽象方法
@Override
public void show(T t) {
System.out.println(t);
}
}
//泛型接口的测试
public class GenericTest3 {
public static void main(String[] args) {
//接口不能实例化,需要new它的实现类
GenericTool3Impl<String> sgt1 = new GenericTool3Impl<>();
sgt1.show("hello");
}
}
执行结果如下:
hello
Process finished with exit code 0