Java泛型
1. 什么是泛型
泛型的本质是参数化类型,泛型提供了编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型,比如要实现一个能够对字符串、整型、浮点型、对象进行大小比较的方法,就可以使用Java泛型。 在不使用泛型的情况下,我们可以通过引用Object类型来是实现参数的任意化,因为在Java中Object类是所有类的父类,可以向集合中添加任意类型的元素,存在风险,在具体使用的时候是需要强制类型转换的。强制类型转换又要求开发者必须明确实际参数的引用类型,不然可能引起强制类型转换错误,且在编译期间是无法被识别的,只能在运行期间被检测出来。这时候,使用泛型就可以在编译期间检查类型是否安全,同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。
2. 泛型的使用
2.1 泛型标记和泛型限定
序号 | 泛型标记 | 泛型说明 |
---|---|---|
1 | E-Element | 在集合中使用,表示在集合中存放的元素 |
2 | T-Type | 表示Java类,包括基本的类和我们自定义的类 |
3 | K-Key | 表示键,比如Map中的key |
4 | V-Value | 表示值 |
5 | N-Number | 表示数值类型 |
6 | ? | 表示不确定的Java类型 |
类型通配符使用“?”表示所有具体的参数类型,例如List<?>在逻辑上就是List |
在使用泛型的时候,若希望将类的继承关系加入到泛型应用找那个,就需要对泛型做限定:
- 对泛型上限的限定:<?extends T> 在Java中使用通配符"?"和"extends"关键字指定泛型的上限,具体用法为<?extends T>,他表示该通配符所代表的类型是T类的子类或者接口T的子接口。
- 对泛型下限的限定:<?super T>
在Java中使用通配符"?"和"super"关键字指定泛型的下限,具体用法为<?super T>,他表示该通配符所代表的类型是T类的父类或者父接口。
2.2 泛型方法
泛型方法指将方法的参数类型定义为泛型,以便在调用时接收不同类型的参数。在方法的内部根据传递给泛型方法的不同参数类型执行不同的处理方法。2.2.1 泛型方法具体示例:
package person.xsc.practice; import java.util.Date; class People{ private String name; People(){} People(String name){ this.name=name; } public String toString() { return name; } } public class GeneralMethodTest { public static <T> void generalMethod(T...inputArray) {//传递多个参数,T表示Java基本的类或者是我们自己定义的类 //增强for循环 for(T element: inputArray) { //instanceof测试它左边的对象是否是它右边的类的实例 if(element instanceof String) { System.out.println("处理基本数据String:"+element); }else if(element instanceof Integer) { System.out.println("处理基本数据Integer:"+element); }else if(element instanceof Boolean) { System.out.println("处理基本数据Boolean:"+element); }else if(element instanceof Float) { System.out.println("处理基本数据Float:"+element); }else if(element instanceof Double) { System.out.println("处理基本数据Double:"+element); }else if(element instanceof Date) { System.out.println("处理基本数据Date:"+element); }else if(element instanceof Long) { System.out.println("处理基本数据Long:"+element); }else if(element instanceof People) { System.out.println("处理自定义People:"+element); } } } public static void main(String[] args) { // TODO Auto-generated method stub generalMethod("你好,世界",158L,23.00,true,8,new People("程序猿"),new Date()); }
} 输出: 处理基本数据String:你好,世界 处理基本数据Long:158 处理基本数据Double:23.0 处理基本数据Boolean:true 处理基本数据Integer:8 处理自定义People:程序猿 处理基本数据Date:Thu May 13 16:52:02 CST 2021
#### 2.2.2 案例
**需求:**
- 定义一个抽象类Goods,类中有抽象方法sell();
- 分别定义类Book、Clothes和Shoes继承Goods,并实现sell(),输出一句话,sell books/sell clothes/sell shoes;
- 定义一个商品销售类,模拟销售,方法:public void sellGoods(List<Goods> goods),循环调用List对象的sell()方法。
Goods类 package person.xsc.practiceII; public abstract class Goods { public abstract void sell(); }
Book类 package person.xsc.practiceII; public class Book extends Goods { @Override public void sell() { // TODO Auto-generated method stub System.out.println("sell books"); } }
Clothes类 package person.xsc.practiceII; public class Clothes extends Goods { @Override public void sell() { // TODO Auto-generated method stub System.out.println("sell clothes"); } }
Shoes类 package person.xsc.practiceII; public class Shoes extends Goods { @Override public void sell() { // TODO Auto-generated method stub System.out.println("sell shoes"); } }
商品销售类 package person.xsc.practiceII; import java.util.List; public class GoodsSeller { public void sellGoods(List<? extends Goods> goods) { //调用集合中 sell() 方法 for(Goods g:goods) { g.sell(); } } }
测试类
package person.xsc.practiceII;
import java.util.ArrayList;
import java.util.List;
public class GoodsTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
List
}
### 2.3 泛型类
泛型类指在定义类时在类上定义了泛型,以便类在使用时可以根据传入的不同参数类型实例化不同的对象。
泛型类的具体使用方法是在类的名称后面添加一个或者多个类型参数的声明部分,在多个泛型参数之间用逗号隔开。
#### 2.3.1 泛型类具体示例
package person.xsc.practice;
class Person{
private String name;
Person(){}
Person(String name){
this.name=name;
}
public String toString() {
return name;
}
}
public class GeneralClassTest
}
public GeneralClassTest(T t) {
this.t=t;
}
@Override
public String toString() {
return "GeneralClassTest [t=" + t + "]";
}
public static void main(String[] args) {
// TODO Auto-generated method stub
GeneralClassTest<Integer> genInt=new GeneralClassTest<Integer>(100);
System.out.println(genInt);
GeneralClassTest<String> genString=new GeneralClassTest<String>("HelloWorld");
System.out.println(genString);
GeneralClassTest<Person> genPerson=new GeneralClassTest<Person>(new Person("程序猿"));
System.out.println(genPerson);
}
} 输出: GeneralClassTest [t=100] GeneralClassTest [t=HelloWorld] GeneralClassTest [t=程序猿]
### 2.4 泛型接口
泛型接口的声明与泛型类的声明相似,通过在接口名后面添加类型参数的声明部分来实现。泛型接口的具体类型一般在实现类中进行声明,不同的类型的实现类处理不同的业务逻辑。
#### 2.4.1 泛型接口具体示例
package person.xsc.practice;
public interface GeneralInterface
package person.xsc.practice;
import java.util.Random;
public class GeneralIntegerImpl implements GeneralInterface
package person.xsc.practice; public class GeneralIntegerTest { public static void main(String[] args) { // TODO Auto-generated method stub GeneralIntegerImpl gi=new GeneralIntegerImpl(); System.out.println(gi.getId()); } }
## 3. 类型擦除
Java在编译期间,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除。因此,泛型主要用于编译阶段。在编译后生成的Java字节码文件中不包含泛型中的类型信息。如在代码中定义List<Object>和List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。
### 3.1 证明Java类型的类型擦除
public class Test {
public static void main(String[] args) {
ArrayList
上面代码中,我们定义了两个ArrayList数组,不过一个是ArrayList<String>泛型类型的,只能存储字符串;一个是ArrayList<Integer>泛型类型的,只能存储整数,最后,我们通过list1对象和list2对象的getClass()方法获取他们的类的信息,最后发现结果为true。说明泛型类型String和Integer都被擦除掉了,只剩下原始类型。