首页 > 其他分享 >JTCR-泛型-12

JTCR-泛型-12

时间:2024-04-22 16:12:25浏览次数:28  
标签:JTCR 12 子类 类型 参数 超类 泛型 Gen

什么是泛型

具有参数化类型的类、接口或方法。具体的类型在运行时才确定。

在泛型出现前通过使用 Object 引用也可以达到泛型的效果,但是缺乏类型安全检查,泛型添加了这一点。

简单的泛型例子

// T 是类型参数,作为实际类型的占位符
class Gen<T> {
  T v;
  
  Gen(T o) {
   v = o;
  }
  
  T getV() {
   return v;
  }
}

class Demo {
  public static void main(String[] args) {
    Gen<Integer> i = new Gen<Integer>(8);
	int v = i.getV();
  }
}

JDK10 开始,不能使用 var 作为类型参数。任意合法的标识符都可以作为类型参数,但默认使用大写单字母,如 T/V/E。

上述代码中,在创建泛型对象时,编译器并没有创建对应版本的 Gen 类,而是移除了所有的泛型信息,使用类型转换使得代码行为仿佛是根据传入的类型创建了一个特定版本的 Gen 类。这个过程称为类型擦除(erasure)。

当把 Gen<Double> 类型的实例赋给 Gen<Integer> 类型的引用时,会发生编译错误。为了防止错误,相同泛型类的不同版本是不兼容的。

传递给类型参数的类型必须是引用类型。

如果不使用泛型,使用 Object 类则在得到类中的值时必须进行类型转换,同时不当的类型转换只有在运行时才能被检查出来。而使用泛型不用显式类型转换,这种错误在编译时能被检查出来。

有两参数的泛型类

class TwoGen<T, V> {
  T o1;
  V o2;
  
  TwoGen(T t, V v) {
    o1 = t;
	o2 = v;
  }
  
  T getO1() {
    return o1;
  }
  
  V getO2() {
    return o2;
  }
}

class Sim {
  public static void main(string[] args) {
    TwoGen<String, Integer> tg = new TwoGen<>("Name", 10);
  }
}

参数类型可以为多个。

限制类型

<T extends superClass>:替换 T 的实际类型必须为超类或超类的任意子类。
可以同时限定一个类和多个接口,替换 T 的类型必须是该类或该类的子类,同时需要实现指定的接口。如

// & 用于分隔类和接口,类必须位于第一个
<T extends ClassName & InterfaceName1>

通配符参数

<?>:通配符,表示未知类型。仅匹配类型参数允许的类型。
<? extends ClassName>:表示该通配符只能匹配 ClassName 类及其子类。
<? super subClass>:表示该通配符只能匹配 subClass 类及其超类。

泛型方法

非泛型类中可以包含泛型方法。一般形式为:

<typeParamList> returnType methodName(paramList) { }

在泛型方法中,类型参数在返回类型前面定义。泛型方法可以是静态,也可以是非静态。

// Comparable 是一个泛型接口,实现了该接口的类是有序的,即可以比较
<T extends Comparable<T>, V extends T> boolean methodName(T a, V b)

调用方法时可以显式指定类型参数,如 className.<Integer, String>methodName(param)
非泛型类中可以使用泛型构造器。

泛型接口

// 定义接口
interface IName<T extends Comparable<T>> {}

// 实现接口,为保证接口的类型参数符合,定义与接口一致,同时直接将类型参数传给接口就行
class CName<T extends Comparable<T>> implements IName<T> {}

实现泛型接口的类如果不是泛型类,需要传递具体类型给接口。否则,实现类必须为泛型类。

// 错误
class Cname implements IName<T> {}

// 正确
class Cname implements IName<Integer> {}

泛型接口可以有不同的数据类型实现;也可以限制实现该接口的数据类型。

raw types 和遗留代码

在泛型出现前,通常使用 Object 代替 T 达到泛型的效果。为了保证没有使用泛型的代码和新的泛型代码一起工作,需要使用 raw types。raw types 就是在创建泛型实例时并不传入实际类型替换 T,此时 T 默认为 Object,与泛型出现之前的方式一致。但这种方法会损失泛型的安全性检查,如果出现类型相关错误,只会在运行时发生,编译时检查不出来。老代码中尽量少用,新代码中不用。

泛型类层次

在类继承层次中,泛型类和非泛型类基本相似,最大的区别是,在泛型类中,超类需要的类型参数需要子类传递给它,即使子类并没有用到类型参数。具体的传递通过构造器。

class Super<T> {
  Super(T o) {}
}
// 子类定义 T 是因为超类需要,通过构造器传递,子类并不需要类型参数。
class Sub<T> extends Super<T> {
  Sub(T o) {
    super(o);
  }
}

// 子类定义了两个类型参数,一个自用,另一个传递给超类。
class Ano<T, V> extends Super<T> {}

非泛型类可以作为泛型类的超类,此时子类不需要传递类型参数给超类。

使用 instanceof 判断一个实例的类型是否为某个泛型类或其子类时,泛型类使用通配符。不能使用特定的类型,因为在运行时,类型信息不可获得。

// 判断 obj 对象的类型是否为泛型类 GenName 或者其子类
obj instanceof GenName<?>

// 错误,不能使用具体类型,运行时类型信息不可获得
obj instanceof GenName<Integer>

泛型类的类型转换仅当实际类型一致时子类可以转换成超类。如 Sub<Integer> 类型的 obj 可以转成 Sup<Integer> 类型 (Sup<Integer>)obj;但不能转成 Sup<String> 类型。

在泛型类继承层次中子类重写超类的方法和非泛型类一致。

泛型类型推断

从 JDK7 开始,提供了简写创建泛型类实例的方法,也可用于方法调用中。

// 具体的类型根据参数调用相应的构造器决定
GenName<Integer> a = new GenName<>(10);

// 方法调用
m(new GenName<>(10));

局部变量类型推导与泛型

// 另一种简写方式
var a = new GenName<Integer>(10)

擦除

Java 语法或者 JVM 的改变必须和泛型之前的老代码兼容,Java 使用了擦除达到这个要求。

Java 源码在编译时,编译器将所有关于泛型类型的信息去除(即擦除),如果泛型是限制类型,使用该限制类型替换类型参数;如果泛型不是限制类型,使用 Object 替换类型参数。例如 <T extends Number><T> 在擦除时分别使用 Number、Object 替换 T。然后使用合适的类型转换保持指定类型的兼容性。这个机制意味着泛型仅在源代码阶段有用,在运行时类型参数不存在。

在泛型类继承层次中,子类重写了超类中的方法,但经过擦除,子类和超类中的方法可能不满足重写的条件。这时编译器会自动添加一个桥接方法(bridge method)使得子类满足重写条件。桥接方法是在字节码层面生成的,不能看也不能显式调用。

class Gen<T> {
  T a;
  
  T get() {
    return a;
  }
}

/* 
  即使子类已指定超类类型为 String,但是经过擦除之后类型变为 Object,
  超类中被重写的方法变为:Object get(), 与子类中的 String get() 返回
  类型不一致,是两个不同的方法。此时,编译器在子类中创建桥接方法,伪码为
  Object get(),在其中调用 String get() 达到重写超类方法的目的。
*/
class Sub extends Gen<String> {
  String get() {
    return a;
  }
}

二义性错误

两个表面上不同的泛型类定义在擦除之后可能是相同的,造成冲突。比如使用不同的类型参数重载方法,如 T get()V get() 在擦除后,都变成了 Object get(),造成冲突。

泛型限制

不能实例化类型参数。如 new T(),因为此时 T 作为占位符,编译器不知道具体类型。

不能在类中定义静态类型参数成员,如 static T astatic T get(),但可以定义静态泛型方法。

不能实例化将类型参数作为类型的数组,如 new T[5];,因为此时编译器无法知道数组的具体类型;不能定义特定泛型类型的数组引用,如 Gen<Integer> a[] = new Gen<Integer>[12];。但是可以定义泛型的数组引用,如 Gen<?> a[] = new Gen<?>[5];

泛型类不能继承 Throwable,也就是不能创建泛型异常类。

参考

[1] Herbert Schildt, Java The Complete Reference 11th, 2019.
[2] https://wiyi.org/bridge-method-in-java.html

标签:JTCR,12,子类,类型,参数,超类,泛型,Gen
From: https://www.cnblogs.com/xdreamc/p/16398584.html

相关文章

  • JTCR-I/O,Try-with-Resources 及其他-11
    I/O基础Java的I/O操作通过流来实现。流是对输入、输出数据的抽象,每个流都和一个具体的物理实体关联,比如在输入中,流可以和键盘、磁盘文件或者网络输入等关联,虽然每个物理实体不同,但是流可以以同样的方式进行处理。Java定义了字节流和字符流。字节流处理的对象是二进制数据,以......
  • Integer超过128要用对象比较,否则出问题
    一、测试代码publicvoidtestEquals(){intint1=12;intint2=12;Integerinteger1=newInteger(12);Integerinteger2=newInteger(12);Integerinteger3=newInteger(127);Integera1=127;//或者写成Integera1=Integ......
  • k8s node节点报错 dial tcp 127.0.0.1:8080: connect: connection refused
    前言在搭建好kubernetes环境后,master节点拥有control-plane权限,可以正常使用kubectl。但其他node节点无法使用kubectl命令,即使同步过去/root/.kube/config文件到各个node节点上,也不行。解决检查KUBECONFIG变量:确保KUBECONFIG环境变量正确设置。KUBECONFIG......
  • C++ 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法
    前言本人一直从事C++上位软件开发工作较多,在之前的项目中通过C++访问西门子PLCS7-200/S7-1200/S7-1500并进行数据交互的应用中一直使用的是ModbusTCP/ModbusRTU协议进行。Modbus上位开源库采用的LibModbus。经过实际应用发现Modbus开源库单次发送和接受的数据不能超......
  • 视野修炼-技术周刊第81期 | Chrome 124 新功能
    欢迎来到第81期的【视野修炼-技术周刊】,下面是本期的精选内容简介......
  • 23201228-第一次Blog
    一、前言:从大一下学期开始学习java到现在,已经完成了三次PTA用java实现的大作业,三次PTA作业的难度在逐渐增大,每次最后一题都是从第一次PTA大作业里迭代而来,难度很大且每次提升,涉及的内容有很多,比如类,方法,Arraylist等,但最主要的还是类的设计,通过这三次作业,很深刻的认识的了设计对于......
  • web server apache tomcat11-12-SSL/TLS Configuration
    前言整理这个官方翻译的系列,原因是网上大部分的tomcat版本比较旧,此版本为v11最新的版本。开源项目从零手写实现tomcatminicat别称【嗅虎】心有猛虎,轻嗅蔷薇。系列文章webserverapachetomcat11-01-官方文档入门介绍webserverapachetomcat11-02-setup启动web......
  • 4.2122数学强基
    4.2021数学强基椭圆一\(MF_1=r,MF_2=2a-r\)\((x+c)^2+y^2=r^2,(x-c)^2+y^2=(2a-r)^2\)分别以两个定点为圆心半径和为定值动圆交点轨迹二\(MF_1=a+m_0-r,MF_2=a-m_0+r\),\(m_0\)为定值\(\sqrt{(x+c)^2+y^2}=a+m_0-r,\sqrt{(x-c)^2+y^2}=a-m_0+r\)\((X+c)^2+Y^2=(a+m_0)^2,......
  • 泛型对象的使用
    泛型对象的使用packagecom.example.core.mydemo.java;importjava.util.Objects;/***output:*null*null*java.lang.Object@3b07d329*0*java.lang.Object@41629346*0*@param<T>*/publicclassObjectTest<T>{public<T>S......
  • 泛型模板化设计DEMO
    泛型模板化设计DEMO1.定义Result泛型类packagecom.example.core.mydemo.java.fanxing;publicclassResult<T>{Tresponse;publicTgetResponse(){returnresponse;}publicvoidsetResponse(Tresponse){this.response=res......