接下来介绍泛型的相关知识
泛型
泛型的作用
讲解:
-
集合不使用泛型的时候,存的时候什么类型都能存。但是取的时候就懵逼了。取出来啥也不是。
public class Demo_01不使用泛型 { public static void main(String[] args) { ArrayList list = new ArrayList(); // 添加元素 list.add("杨颖"); list.add("赵丽颖"); list.add("迪丽热巴"); list.add(18); list.add(99.8); // 循环遍历获取元素 for (Object obj : list) { // 操作元素--->打印输出每个字符串名字的长度 String name = (String)obj;// 很容易发生类型转换异常,ClassCastException System.out.println("名字的长度: "+name.length()); } } }
-
使用泛型
- 使用泛型在编译期直接对类型作出了控制,只能存储泛型定义的数据
public class Demo02_使用泛型 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); // 添加元素 list.add("杨颖"); list.add("赵丽颖"); list.add("迪丽热巴"); // list.add(18);// 编译报错 //list.add(99.8);// 编译报错 } }
-
泛型:可以在类或方法中预知地使用未知的类型。
tips:泛型的作用是在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
定义和使用含有泛型的类
讲解
定义含有泛型的类
定义格式:
修饰符 class 类名<代表泛型的变量> { }
代表泛型的变量: 可以是任意字母 例如: T,E...
泛型在定义的时候不具体类型,使用的时候才具体类型。在使用的时候确定泛型的具体数据类型。
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
....
}
确定泛型具体类型
在创建对象的时候确定泛型
例如,ArrayList<String> list = new ArrayList<String>();
此时,变量E的值就是String类型,那么我们的类型就可以理解为:
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
...
}
代码
public class MyGenericClass<E> {
// 泛型类什么时候要定义:
// 当类中很多的方法的参数类型或者返回值类型,在定义的时候不确定是什么类型,那么就可以把该类定义成泛型类
// 那么类中方法的参数类型和返回值类型就可以使用泛型来代替
public void method1(E e){
}
public E method2(E e){
return e;
}
}
public class Test {
public static void main(String[] args) {
// 使用jdk提供的泛型类
ArrayList<String> list1 = new ArrayList<>();
list1.add("张三");
String e = list1.get(0);
// 使用自定义的泛型类
MyGenericClass<String> mgc = new MyGenericClass<>();
mgc.method1("张三");
String res1 = mgc.method2("李四");
MyGenericClass<Integer> mgc2 = new MyGenericClass<>();
mgc2.method1(19);
Integer i = mgc2.method2(10);
}
}
定义和使用含有泛型的方法
定义含有泛型的方法
定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
例如,
public class MyGenericMethod {
// 使用场景:当定义某个方法,该方法的参数或者返回值类型无法确定的时候,就可以把该方法定义成泛型方法
// 然后使用该泛型来表示参数的类型或者返回值类型
public <T> T method1(T t){
return t;
}
}
确定泛型具体类型
调用方法时,确定泛型的类型
public class GenericMethodDemo {
public static void main(String[] args) {
// 创建对象
MyGenericMethod mgm = new MyGenericMethod();
String res1 = mgm.method1("itheima");// 确定泛型的具体数据类型为String
Integer res2 = mgm.method1(199);// 确定泛型的具体数据类型为Integer
}
}
定义和使用含有泛型的接口
定义含有泛型的接口
定义格式:
修饰符 interface接口名<代表泛型的变量> { }
例如,
public interface MyGenericInterface1<E> {
// 使用场景:当接口中很多方法的参数类型或者返回值类型,无法确定具体的数据类型,那么在定义接口的时候,
// 可以把接口定义成含有泛型的接口,然后接口中的方法就可以使用该泛型来表示未知的参数类型或者返回值类型
public abstract void method1(E e);
public abstract E method2(E e);
//....
}
确定泛型具体类型
使用格式:
1、定义实现类时确定泛型的类型
例如
public class MyGenericImp1 implements MyGenericInterface1<String> {
@Override
public void method1(String s) {
}
@Override
public String method2(String s) {
return null;
}
}
此时,泛型E的值就是String类型。
2、始终不确定泛型的类型,直到创建实现类对象时,确定泛型的类型
例如
public class MyGenericImp2<E> implements MyGenericInterface1<E> {
@Override
public void method1(E e) {
}
@Override
public E method2(E e) {
return null;
}
}
确定泛型:
/*
* 使用
*/
public class GenericInterface {
public static void main(String[] args) {
MyGenericImp2<String> mg2 = new MyGenericImp2<>();
mg2.method1("itcast");
mg2.method2("itheima");
}
}
小结
- 定义含有泛型的接口: 修饰符 interface 接口名<代表泛型的变量> { }
- 使用含有泛型的接口:
- 实现类实现接口的时候,确定泛型的具体数据类型
- public class 类名 implements 接口名<具体的引用数据类型>{}
- 实现类实现接口的时候,不确定泛型的具体数据类型,而是创建实现类对象的时候确定
- public class 类名 implements 接口名{}
- 实现类实现接口的时候,确定泛型的具体数据类型
- 泛型是一种未知的数据类型,定义在类上的泛型,使用类的时候会确定泛型的类型,定义在方法上的泛型,会在使用方法的时候确定泛型,定义在接口上的泛型,需要使用接口的时候确定泛型。
泛型通配符
通配符基本使用
泛型的通配符:在确定泛型的具体数据类型的时候,不知道使用什么类型,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
例如:
public class Test1 {
public static void main(String[] args) {
/*
泛型通配符:
概述:在确定泛型的具体数据类型的时候,不知道使用什么类型,此时可以使用?,?表示未知通配符。
注意:
1.泛型不存在多态
2.使用了泛型通配符集合,不能使用增删操作
*/
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
// 调用method方法,传入list1
method1(list1);
// 调用method方法,传入list2
method1(list2);
// 调用method方法,传入list3
method1(list3);
// ArrayList<Object> list = new ArrayList<String>();// 编译报错
}
public static void method1(ArrayList<?> list){
//list.add("jack");// 编译报错
for (Object obj : list) {
System.out.println(obj);
}
}
}
通配符高级使用----受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式:
类型名称 <? extends 类 > 对象名称
- 意义:
只能接收该类型及其子类
泛型的下限:
- 格式:
类型名称 <? super 类 > 对象名称
- 意义:
只能接收该类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
public class Test2 {
public static void main(String[] args) {
/*
通配符高级使用----受限泛型:
上限: <? extends 类名> 只能接受该类型或者其子类类型
下限: <? super 类名> 只能接受该类型或者其父类类型
*/
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Number> list4 = new ArrayList<>();
// 关系: String继承Object类,Integer类继承Number类,Number类继承Object类
//method1(list1);// 编译报错
//method1(list2);// 编译报错
method1(list3);
method1(list4);
System.out.println("============================");
method2(list1);
//method2(list2);// 编译报错
method2(list3);
method2(list4);
}
// 定义一个方法,只可以接收list3和list4
public static void method1(ArrayList<? extends Number> list){
}
// 定义一个方法,只可以接收list3和list4,list1
public static void method2(ArrayList<? super Integer> list){
}
}
小结
- ?是泛型通配符,当在确定泛型具体数据类型的时候,无法确定具体的数据类型,就可以使用?这个泛型通配符
- 如果要对?泛型通配符的取值范围进行限制,可以使用泛型限定
- 上限: <? extends 类>
- 下限:<? super 类>