泛型目录
泛型的引入
List集合中是可以加入不同类型的元素的,比如下面这样
public class demo1 {
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("java");
list.add(12);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object obj = iterator.next();
String s = (String)obj;
System.out.println(s);
}
}
}
这段代码在编译的过程中是没有报错的,但是一旦运行就会报 ClassCastException: 类型转换异常的错误
究其原因,是因为list集合中既有String类型的元素,又有Integer类型的元素
这样会在编译和操作集合的过程造成很多麻烦,这时候就会希望集合也能跟数组一样,可以在一开始就要求只能加入某一种类型的元素
这样在添加元素的时候,只能添加定义好的类型的元素
这样的技术就叫做泛型
一般来说,泛型主要用于集合中
泛型:把明确数据类型的工作提前到编译时期,在创建对象的时候明确。
这种操作有点像把类型当作参数进行传递,所以泛型还有另外一种叫法:参数化类型。
格式:<数据类型>
注意:这里的数据类型只能是引用数据类型
使用泛型的好处:
- 将运行时期的问题提前到编译时期
- 避免了强制类型转化
- 优化了代码程序,消除不必要的警告内容
public class demo1 {
public static void main(String[] args) {
//使用了泛型的集合
List<String> list1 = new ArrayList<>();
//ArrayList后面的<>里面的String可以省略,原来为new ArrayList<String>();
list1.add("java");
list1.add("hive");
list1.add("flume");
//Iterator后面也需要加上泛型
Iterator<String> it = list1.iterator();
while(it.hasNext()){
//避免了强制类型转换
String s = it.next();
System.out.println(s);
}
}
}
当传入的参数是一个类的时候
public class demo2 {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("zhang",12));
list.add(new Student("zhao",15));
list.add(new Student("zhou",18));
Iterator<Student> iterator = list.iterator();
while(iterator.hasNext()){
Student s = iterator.next();
System.out.println(s.getName()+"---"+s.getAge());
}
}
}
其实我们在api中查阅相关内容的时候,就应该已经见过泛型的相关用法了
这里的就是泛型的表现
为什么这里的<>里面是一个E,而不是明确的String,Integer等引用数据类型呢?
因为这里的E仅仅表示的是一种参数类型,这个参数类型是一个变量,可以指代任意一种引用数据类型,具体是哪种根据实际使用的时候传入的数据类型。
而这个既然是变量,只要符合变量的命名规则就是可以的,为了有辨识度,通常使用E或者T来作为括号里面的内容
泛型的高级用法:(通配符)
泛型通配符< ? > —— 任意类型,如果没有明确,那么就是Object以及任意的Java类了
< ? extends E> —— 向下限定,E,与E同级的类及其子类
< ? super E> —— 向上限定,E,与E同级的类及其父类
public class demo3 {
public static void main(String[] args) {
ArrayList<?> arrayList = new ArrayList<>();
//泛型通配符<?>
//表示任意类型,可以是Object以及任意的Java类
ArrayList<?> objects1 = new ArrayList<Object>();
ArrayList<?> objects2 = new ArrayList<Animal>();
ArrayList<?> objects3 = new ArrayList<Dog>();
//<? extends E> 向下限定,E及其子类
ArrayList<? extends Animal> list1 = new ArrayList<Animal>();
ArrayList<? extends Animal> list2 = new ArrayList<Dog>();
ArrayList<? extends Animal> list3 = new ArrayList<Cat>();
//<? super E> 向上限定,E及其父类
ArrayList<? super Animal> list11 = new ArrayList<Animal>();
ArrayList<? super Animal> list22 = new ArrayList<Object>();
//错误写法ArrayList<? super Animal> list33 = new ArrayList<Dog>();
}
}
泛型类
泛型类:把泛型定义在类上面
package test.GenericDemo;
//这里的T是一个参数,可以指代任意的引用数据类型
//这个类的setObj方法可以传入String,Integer等多种数据类型
public class genericDemo1<T> {
private T obj;
public void setObj(T obj){
this.obj = obj;
}
public T getObj(){
return obj;
}
}
写一个类来使用泛型类
package test.GenericDemo;
public class genericTest1 {
public static void main(String[] args) {
//虽然我们定义了一个泛型类,但是也可以不去使用它
genericDemo1 g1 = new genericDemo1();
//此时g1可以传入任意的参数
g1.setObj("hello");
System.out.println(g1.getObj()); //hello
g1.setObj(20);
System.out.println(g1.getObj()); //20
/*
虽然上面都成功的打印出了结果,但实际上,这里的hello与20都是 Object 类型
尝试一下下面这条语句
String s= g1.getObj();
如果这样写就会报错,错误原因是Object无法转换为String类型
因此如果我们只需要String类型的话,就需要使用泛型来定义
*/
genericDemo1<String> g2 = new genericDemo1<>();
g2.setObj("hello");
String s = g2.getObj();
System.out.println(s); //hello,结果正确
}
}
泛型方法
泛型方法:将泛型定义方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 变量名)
先来看看如果不使用泛型,写一个传入多种数据类型的方法是什么样子
public class genericDemo2 {
public void show(String s){
System.out.println(s);
}
public void show(Integer i){
System.out.println(i);
}
public void show(Boolean b){
System.out.println(b);
}
}
写一个类来测试
public class genericTest2 {
public static void main(String[] args) {
genericDemo2 g1 = new genericDemo2();
g1.show("hello"); //hello
g1.show(12); //12
g1.show(true); /true
}
}
结果是正确的,但如果我们每需要传入一种数据类型,就需要写一个对应的方法,这样未免太过麻烦
这时候就用到泛型方法了
格式:public <泛型类型> 返回类型 方法名(泛型类型 变量名)
package test.GenericDemo;
//泛型方法
public class genericDemo2 {
public <T> void show(T t){
System.out.println(t);
}
}
写一个测试类
package test.GenericDemo;
public class genericTest2 {
public static void main(String[] args) {
genericDemo2 g2 = new genericDemo2();
g2.show("hello"); //hello
g2.show(12); //12
g2.show(12.23); //12.23
g2.show(true); //true
}
}
同样可以打印这些对应的结果,但是省却了很多代码
泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…>
接口
public interface genericDemo3<T> {
public abstract void show(T t);
}
实现接口的类
public class genericImpl<T> implements genericDemo3<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
具体的测试类
package test.GenericDemo;
public class genericTest3 {
public static void main(String[] args) {
genericImpl<String> g1 = new genericImpl();
g1.show("hello"); //hello
genericImpl<Integer> g2 = new genericImpl<>();
g2.show(12); //12
}
}
实例
选择学生中年龄大于30岁的学生对象
package lambdaDemo;
public interface filterInterface<Student> {
public boolean judge(Student s);
}
这里的类名filterStudentByAge后面不能加上< Student >,加上就会报错
package lambdaDemo;
//这里的类名后面不能加上<>,加上就会报错
public class filterStudentByAge implements filterInterface<Student> {
public boolean judge(Student s) {
return s.getAge() > 30;
}
}
public List<Student> filterStudent(List<Student> list, filterInterface<Student> f){
List<Student> list1 = new ArrayList<Student>();
for(Student s : list){
if(f.judge(s)){
list1.add(s);
}
}
return list1;
}
@Test
public void show2(){
List<Student> list2 = filterStudent(list,new filterStudentByAge());
for(Student s : list2){
System.out.println(s);
}
}
直接用测试方法测试