Java 高级特性泛型
2020/3/23 17:02:00
本文主要是介绍Java 高级特性泛型,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概述
Generics(泛型),我们经常在Java集合或者框架里面经常看见对泛型的使用场景,那么泛型的作用有哪些呢。
- 可以帮助我们处理多种数据类型执行相同的代码
- 数据安全性,泛型中的类型在使用时指定类型后,不需要强制类型转换,最早起Java是没有泛型的,它是使用Object来代替的,这样程序员在写程序的时候很容易出现类型转换的错误。
泛型的本质是为了参数化类型,就是将类型由原来的具体的类型参数化,就像方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型类和泛型接口
泛型类/接口的定义,使用一个类型变量T(其他大写字母都可以,不过常用的就是T,E,K,V等等),相当于一个占位符,并且用<>括起来,并放在类/接口名的后面。泛型类是允许有多个类型变量的
public class GenericsClass<T> { private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ GenericsClass<String> genericsClass = new GenericsClass<>(); genericsClass.print(); } } 复制代码
public class GenericsClass<T,V> { private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ GenericsClass<String,Integer> genericsClass = new GenericsClass<>(); genericsClass.print(); } } 复制代码
public interface ImpGenerics<V> { } 复制代码
而实现泛型接口的类,有两种实现方法
public class Generics<T> implements ImpGenerics<T> { } 复制代码
这中实现的时候,没有指定具体的类型,这种实现方式在创建对象的时候,需要传入具体的类型
public class Generics implements ImpGenerics<String> { } 复制代码
这种是在实现的时候传入具体的实参,创建对象的时候就想普通类一样使用就OK
泛型方法
泛型方法,是在调用方法的时候指明泛型的具体类型 ,泛型方法可以在任何地方和任何场景中使用,包括普通类和泛型类,
public class GenericsClass<K,V> { private K date; private V value; //普通方法 private V getValue(K date){ return value; } //泛型方法 private <T> T genericsMethod(T date){ return date; } public GenericsClass(K date, V value) { this.date = date; this.value = value; } private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ GenericsClass<String,Integer> genericsClass = new GenericsClass<>("k",123); genericsClass.print(); int a = genericsClass.getValue("k"); System.out.println("v="+a); } } 复制代码
泛型方法必须通过“<类型占位符>”来声明返回的类型,譬如<V>等
泛型限定类型变量
通常在使用时候,我们需要让所有的类型具体同一个方法,我们需要对类型变量加以约束,比如计算两个变量的最小,最大值,为了确保传入的两个变量一定有compareTo方法?我们就需要将T限制为实现了接口Comparable的类
private <T extends Comparable> T min(T a,T b){ return a.compareTo(b)>0 ? b:a; } 复制代码
T extends Comparable中 T表示应该绑定类型的子类型,Comparable表示绑定类型,子类型和绑定类型可以是类也可以是接口,同时extends左右都允许有多个,如 T,V extends Comparable & Serializable 注意限定类型中,只允许有一个类,而且如果有类,这个类必须是限定列表的第一个。 这种类的限定既可以用在泛型方法上也可以用在泛型类上
private <T extends Comparable & Serializable> T min(T a, T b){ return a.compareTo(b)>0 ? b:a; } 复制代码
泛型中的约束和局限性
- 不能用基本类型实例化类型参数
public class GenericsClass<K,V> { private K date; private V value; //普通方法 private V getValue(K date){ return value; } //泛型方法 private <T> T genericsMethod(T date){ return date; } private <T extends Comparable & Serializable> T min(T a, T b){ return a.compareTo(b)>0 ? b:a; } public GenericsClass(K date, V value) { this.date = date; this.value = value; } private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ //不能使用基本类型 GenericsClass<String,int> genericsClass = new GenericsClass<>("k",123); } } 复制代码
- 运行时类型查询只适用于原始类型
public class GenericsClass<K> { private K date; private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ GenericsClass<String> genericsClass = new GenericsClass<>(); GenericsClass<String> genericsClass_ = new GenericsClass<>(); System.out.println("泛型类型genericsClass="+genericsClass_.getClass().getName().toString()); System.out.println("泛型类型genericsClass_="+genericsClass_.getClass().getName().toString()); } } 复制代码
输出
泛型类型genericsClass=com.mtx.javalib.GenericsClass 泛型类型genericsClass_=com.mtx.javalib.GenericsClass 复制代码
- 泛型类的静态上下文中类型变量失效
不能在静态域或方法中引用类型变量。因为泛型是要在对象创建的时候才知道是什么类型的,而对象创建的代码执行先后顺序是static的部分,然后才是构造函数等等。所以在对象初始化之前static的部分已经执行了,如果你在静态部分引用的泛型,那么毫无疑问虚拟机根本不知道是什么东西,因为这个时候类还没有初始化。 4. 不能创建参数化类型的数组
public class GenericsClass<K> { private K date; private void print(){ System.out.println("泛型类型"); } public static void main(String [] args){ //编译会报错 GenericsClass<String>[] genericsClass = new GenericsClass<String>()[3]; } } 复制代码
为什么是这样,主要是Java的泛型实现方式有关,后续会说到
- 不能实例化类型变量
- 不能捕获泛型类的实例
public <T extends Throwable> T testMethod(T t){ try { System.out.println("泛型类型异常无法捕获"); }catch (T e){ } return t; } 复制代码
但是这种方式是可以的
public <T extends Throwable> T testMethod(T t){ try { System.out.println("泛型类型异常无法捕获"); }catch (Throwable e){ } return t; } 复制代码
泛型类型的继承规则
泛型类可以继承或者扩展其他泛型类,比如List和ArrayList
通配符类型
一般来说泛型的通配符有两种,
- ? extends X
表示类型的上界,类型参数是X的子类或自身 - ? super X
表示类型的下界,类型参数是X的超类
public class Car { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } public class Bmw extends Car { @Override public String getName() { return super.getName(); } @Override public void setName(String name) { super.setName(name); } } 复制代码
public class Bmw extends Car { @Override public String getName() { return super.getName(); } @Override public void setName(String name) { super.setName(name); } public static void testMethod(GenericsClass<? extends Car> car){ System.out.println("类型参数只能是Car的子类"); } public static void main(String[] args){ GenericsClass<Bmw> genericsClass = new GenericsClass(); testMethod(genericsClass); } } 复制代码
Java 泛型原理
Java语言中的泛型一般称为伪泛型,它只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转型代码,因此,对于运行期的Java语言来说,ArrayList<int>与ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型
由于Java泛型的引入,各种场景(虚拟机解析、反射等)下的方法调用都可能对原有的基础产生影响和新的需求,如在泛型类中如何获取传入的参数化类型等。因此,JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题,Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名[3],这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。修改后的虚拟机规范要求所有能识别49.0以上版本的Class文件的虚拟机都要能正确地识别Signature参数。
另外,从Signature属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据
这篇关于Java 高级特性泛型的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求
- 2024-10-01使用 golang 将ETH账户的资产平均分散到其他账户
- 2024-10-01JWT用户校验课程:从入门到实践
- 2024-10-01Server Component课程入门指南