泛型的概述
由来:
在Java中我们在声明方法时,当在完成方法功能时如果有未知的数据需要参与,这些未知的数据需要在调用方法时才能确定,那么我们把这样的数据通过形参表示。那么在方法体中,用这个形参名来代表那个未知的数据,而调用者在调用时,对应的传入值就可以了。
受以上两启发,JDK1.5设计了泛型的概念。泛型即为“类型参数”,这个类型参数在声明它的类、接口或方法中,代表未知的通用的类型。<类型>这种语法形式就叫泛型。
泛型的定义
- 一种未知的数据类型,在使用的时候确定其具体的数据类型。
泛型的好处
- 集合不使用泛型的时候,存的时候什么类型都能存。但取出来的时候,需要做类型判断,复杂繁琐也容易出错。使用泛型在编译期直接对类型作出了控制,只能存储泛型定义的数据。如果有了泛型并使用泛型,那么既能保证安全,又能简化代码。因为把不安全的因素在编译期间就排除了;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。
在哪里可以声明类型变量<T>
在方法丶类丶接口上可以声明泛型
- 声明类或接口时,在类名或接口名后面声明类型变量,我们把这样的类或接口称为泛型类或泛型接口
- 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是声明不是单纯的使用)了类型变量的方法称为泛型方法
含有泛型的类
语法格式:
注意:
- <代表泛型的变量>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:<T>、<K,V>等。
- <代表泛型的变量>中的类型变量不能用于静态成员上。
应用场景
- 当类中的成员变量或者成员方法的形参类型\返回值类型不确定的时候,就可以把该类定义为含有泛型的类
?代码示例
//定义含有泛型的类
class MyArrayList<E>{
//含有泛型的变量
E e;
//含有泛型的方法
public void add(E e){
System.out.println("含有泛型的方法");
}
//含有泛型的方法
public E get(E index){
return index;
}
}
怎么使用含有泛型的类
创建该类对象的时候,确定该类泛型的具体数据类型。泛型的具体数据类型必须是引用数据类型,不能是基本数据类型。例如,ArrayList<String> list = new ArrayList<String>( )? ?此时,变量E的值就是String类型,那么我们的类型就可以理解为:
class ArrayList<String>{
public boolean add(String e){ }
public String get(int index){ }
...
}
?下面我们来使用自定义的泛型类。
public class Test {
public static void main(String[] args) {
//泛型字符串类型
MyArrayList<String> s1 = new MyArrayList<>();
//泛型是Integer类型
MyArrayList<Integer> s2 = new MyArrayList<>();
//MyArrayList<double> s3 = new MyArrayList<>();// 错误,泛型必须是引用数据类型
}
}
含有泛型的方法
语法格式:
?例如:java.util.Arrays类中的泛型方法
public static <T> List<T> asList(T... a){
....
}
我们自定义含有泛型的方法
public class Demo {
// 定义含有泛型的方法
public static <T> T method1(T t){
return t;
}
}
应用场景
- ?如果一个类中,某个方法的参数类型或者返回值类型不确定的时候,可以把该方法定义为含有泛型的方法
怎么使用含有泛型的方法
class Test1{
// 定义含有泛型的方法
public static <T> T method(T t){
return t;
}
public static void main(String[] args) {
// 指定泛型的具体数据类型为Integer
Integer i = method(100);
System.out.println(i);// 100
//指定泛型的具体数据类型为String
String s = method("it");//
System.out.println(s);
}
}
含有泛型的接口
语法格式:
自定义含有泛型的接口
public interface IA<E> {
public abstract void method1(E e);
public default E method2(E e){
return e;
}
}
如何使用含有泛型的接口:
方式一:定义实现类时确定泛型的类型
// 通过实现类的方式确定接口泛型的具体数据类型
public class Imp1 implements IA<String> {
@Override
public void method1(String s) {
}
@Override
public String method2(String s) {
return null;
}
}
此时,泛型E的值就是String类型。
方式二:始终不确定泛型的类型,直到创建对象时,确定泛型的类型
/*
实现类实现接口的时候不确定接口泛型的具体数据类型,
而是创建实现类对象的时候确定接口泛型的具体数据类型
*/
class Imp2<E> implements IA<E> {
@Override
public void method1(E e) {
System.out.println("实现类 method1");
}
@Override
public E method2(E e) {
return e;
}
}
class Test {
public static void main(String[] args) {
// 创建实现类对象的时候确定接口泛型的具体数据类型
Imp2<String> imp1 = new Imp2<>();
imp1.method1("it");
String s1 = imp1.method2("it");
System.out.println(s1);// it
Imp2<Integer> imp2 = new Imp2<>();
imp2.method1(100);
Integer i = imp2.method2(100);
System.out.println(i);// 100
}
}
泛型通配符
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。此时只能接受数据,不能往该集合中存储数据。
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
// 关系:String继承Object,Integer继承Number,Number继承Objec
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Number> list4 = new ArrayList<>();
list2.add("张三");
list2.add("李四");
list2.add("王五");
method2(list2);
method1(list2);
}
// 定义一个方法,可以接收以上4个集合
public static void method1(ArrayList list){
Object obj = list.get(0);
list.add("jack");
System.out.println("obj:"+obj);//obj:张三
System.out.println("list:"+list);// list:[张三, 李四, 王五, jack]
}
/*
通配符基本使用:
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
注意: 不能往该集合中存储数据,只能获取数据.
*/
public static void method2(ArrayList<?> list){
Object obj = list.get(0);
//list.add("jack");// 编译报错,不能往该集合中存储数据,只能获取数据.
System.out.println("obj:"+obj);// obj:张三
System.out.println("list:"+list);// list:[张三, 李四, 王五]
}
}
受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
- 格式: 类型名称 <? extends 类 > 对象名称
- 意义: 只能接收该类型及其子类
泛型的下限:
- 格式: 类型名称 <? super 类 > 对象名称
- 意义: 只能接收该类型及其父类型
import java.util.ArrayList;
public class Demo01 {
public static void main(String[] args) {
/*
通配符高级使用----受限泛型:
上限: <? extends 类名> 只能接收该类类型或者其子类类型
下限: <? super 类名> 只能接收该类类型或者其父类类型
*/
// 关系:String继承Object,Integer继承Number,Number继承Objec
ArrayList<Object> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Integer> list3 = new ArrayList<>();
ArrayList<Number> list4 = new ArrayList<>();
//因为 Integer继承Number
method1(list4);
method1(list3);
//Integer继承Number,Number继承Object
methoh2(list1);
methoh2(list3);
methoh2(list4);
}
// 定义一个方法,只可以接收只能接收Number类型或者其子类类型集合
public static void method1(ArrayList<? extends Number> list) {
}
// 定义一个方法,只能接收Integer类型或者其父类类型
public static void methoh2(ArrayList<? super Integer> list) {
}
}
|