【Java基础】6.泛型

embedded/2024/11/13 5:31:18/

《Java基础》目录

例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 《Java基础》目录
    • 6. 泛型
      • 6.1 泛型类
      • 6.2 泛型方法
      • 6.3 类型变量的限定
      • 6.4 泛型代码和虚拟机
        • 6.4.1 类型擦除
        • 6.4.2 转换泛型表达式
        • 6.4.3 转换泛型方法
        • 6.4.4 总结
      • 6.5 限制与局限性
        • 6.5.1 不能用基本类型实例化类型参数
        • 6.5.2 运行时类型查询只适用于原始类型
        • 6.5.3 不能创建参数化类型的数组
        • 6.5.4 `Varargs`警告
        • 6.5.5 不能实例化类型变量
        • 6.5.6 泛型类的静态上下文中类型变量无效
        • 6.5.7 不能抛出或捕获泛型类的实例
        • 6.5.8 可以取消检查型异常的检查
        • 6.5.9 擦除后的冲突
      • 6.6 泛型类型的继承
      • 6.7 通配符类型
        • 6.7.1 通配符概念
        • 6.7.2 通配符的超类限定
        • 6.7.3 无限定通配符
      • 6.8 反射和泛型
        • 6.8.1 泛型`Class`类


6. 泛型

泛型可以使代码对不同类型的对象重用。

6.1 泛型类

泛型类就是有一个或多个类型变量的类。该节使用Pair类进行示范,该类使我们只需要关注泛型,而不用为数据存储细节而分心。

java">public class Pair<T>
{private T first;private T second;public Pair(T f,T s){first=f;second=s;}public T getFirst(){return first;}
}

Pair类引入了类型变量T,用尖括号括起来,放在类名的后面。泛型类可以有多个类型变量,用不同的变量表示。如public class Pair<T,U>

一般情况类型变量用短且大写的字母表示。

  • E:表示集合的元素类型
  • K,V:分别表示键和值的类型
  • T:必要时可以使用相邻的字母U或S表示任意类型

6.2 泛型方法

除了定义泛型类,该可以在普通类中定义带类型参数的泛型方法。

java">class ArrayAlg{public static <T> T getMiddle(T... a){return a[a.length/2];}
}

类型变量要放在修饰符的后面,并且放在返回类型的签名。

当进行调用的时候,可以把具体类型放在尖括号中,放在方法名签名:

String s =ArrayAlg.<String>getMiddle("a","b","c")

在这种情况下,可以省略类型参数,编译器可以直接进行类型转换。

String s=ArrayAlg.getMiddle("a","b","c")

这个利用了Java的自动装箱机制,但当同时出现了不同类型的参数的时候,有可能会导致报错,只需要调整一下传递的参数类型即可。

6.3 类型变量的限定

如果定义了一个比较大小的泛型方法,但是如何确定这个具体类型中具有comparaTo的比较方法呢?

只需要在定义泛型方法的时候,对类型变量进行一个限定来实现这一要求。

public static <T extends Comparable> T min(T[] a)

实际上Comparable接口本身就是一个泛型类型,而这里使用的是extend而非implements

这里的extends是因为限定类型更接近于子类型的概念。同时多个限定可以用&来连接

6.4 泛型代码和虚拟机

虚拟机没有泛型类型,只有普通类,所以编译器在编译过程中会进行类型擦除。

6.4.1 类型擦除

无论如何定义一个泛型类型,都会自动提供一个与其对应的普通类,此时类型变量就会被擦除,替替换为其限定类型(若无限定类型则是Object类)。

6.4.2 转换泛型表达式

编写泛型方法时除了进行类型擦除,还有对其进行强制类型转换。

比如

java">Pair<Empty> b=...
Empty d=b.getFirst();

其中getFirst进行类型擦除后返回的是Object类型,编译器自动插入转换到Empty的强制类型转换。

访问同一个泛型字段的时候也要进行强制类型转换。

6.4.3 转换泛型方法

类型擦除也会出现在泛型方法中。

例如:

public static <T extends Comparable> T min(T[] a)

进行类型擦除后只剩下一个方法

public static Comparable min(Comparable[] a)

类型擦除会和多态产生冲突

  1. 没有返回值类型
java">class DateInterval extends Pair<LocalDate>{public void setFirst(LocalDate f){if(f.getFirst()>=0)super.setFirst(f);}
}

类型擦除后会出现两个同名方法:

  • public void setFirst(LocalDate f)
  • public void setFirst(Object f)

为解决这个方法,编译器在DateInterval类中生成一个桥方法

public void setFirst(Object f){setFirst((LocalDate) f)}

  1. 带有返回值类型

虚拟机中会由参数类型和返回值类型共同指定一个方法。所以如果只使用桥方法,在下面的情况就会很奇怪。

java">class DateInterval extends Pair<LocalDate>{public LocalDate getFirst(){return (LocalDate) super.getFirst();}
}

但是就会出现两种方法:

  • public LocalDate getFirst()
  • public Object getFirst()

这种情况可以通过对不同的方法生成字节码,虚拟机就可以正确的处理这种情况。

6.4.4 总结

对于Java的类型转换,会遇到一下事实:

  • 虚拟机中没有泛型,只有普通的类和方法
  • 所有的类型参数都会替换为他们的限定类型
  • 会合成桥方法在类型擦除下保持多态
  • 为保持类型安全性,必要时会插入强制类型转换

6.5 限制与局限性

Java中泛型类型的限制,大部分都是由于编译器进行类型擦除所引起的。

6.5.1 不能用基本类型实例化类型参数

不能用基本类型替代类型参数,所以没有Pair<double>只有Pair<Double>

原因就是类型擦除后的Pair类只有Object字段,而Object不能存储double值。

6.5.2 运行时类型查询只适用于原始类型

虚拟机中的对象总有一个特定的非泛型类型。因此所有类型查询只产生原始类型。

例如a instanceof Pair<String>实际上测试a instanceof Pair<T>或强制类型转换

为了提示这一风险,在对泛型类型进行查询时,会得到一个编译器错误,或是警告。

6.5.3 不能创建参数化类型的数组

不能实例化参数化类型的数组。table=new Pair[10]

因为执行类型擦除后,就会变成Object[],数组会记住他的数据类型,如果数组试图存储其他类型的元素,就会抛出异常。

可以声明但不可以实例化创建

6.5.4 Varargs警告

向参数可变长方法传递泛型参数。

为了调用这个方法,Java虚拟机创建一个数组来存放参数,就违反了不能实例化类型数组的规则。但不会报错,只会提供警告。

可以采用两种方法来解决。

  1. 为调用的方法增加注解SuppressWarnings("unchecked")
  2. 直接使用SafeVarargs注解(该注解只能用于声明为staticfinalprivate的构造器或方法)
6.5.5 不能实例化类型变量

不能使用诸如new T()的构造方法,因为不会有人希望会new一个Object对象。

java8之后,最好的解决办法就是提供一个构造器表达式,比如

Pair<String> p=Pair.makePair(String::new);

该方法接受一个Supplier<T>,这是一个函数式接口,表示一个无参函数且返回类型为T的函数。

传统的方法是使用Java的反射机制,同时不能使用T.class,需要适当设计API

6.5.6 泛型类的静态上下文中类型变量无效

不能在静态字段或方法中引用类型变量。例如

java">public class Singleton<T>{private static T singleInstance;
}

进行类型擦除后,只会剩下包含一个singleInstance字段的Singleton类,所以禁止使用带有类型变量的静态字段和方法。

6.5.7 不能抛出或捕获泛型类的实例

既不能抛出也不能捕获泛型类的对象,实际上泛型类可以扩展Throwable甚至都不是合法的。

catch语句中不能使用类型变量,因为会导致不能编译。

但是在异常规范中,使用类型变量是允许的。

不允许:

java">public static <T extends Throwable> void do(){try{do...}catch(T e){...}
}

允许:

java">public static <T extends Throwable> void do(){try{do...}catch(Throwable e){...}
}
6.5.8 可以取消检查型异常的检查

Java异常处理的原则就是,必须为所有检查型异常提供一个处理器。不过可以利用泛型取消该机制。

使用该方法

java">@SuppressWarnings("unchecked")
static <T extends Throwable> void throws(Throwable t) throws T{throw (T) t;
}

通过使用泛型类,擦除和注解,实现欺骗编译器,使其将异常识别为非检查型异常,达到消除Java类型系统的部分基本限制。

6.5.9 擦除后的冲突

当泛型类型被擦除后,不允许创建引发冲突的条件。

为了支持擦除转换,倘若两个接口类型是同一个接口的不同参数化,一个类或类型变量就不能同时作为这两个接口类型的子类。

6.6 泛型类型的继承

泛型类可以继承泛型类,但是泛型类的具体类型没有继承关系。

比如:ManagerEmployee是继承关系,但是Pair<Manager>Pair<Employee>没有继承关系。

同理,ArrayList<T>继承了List<T>,但是ArrayList<Manager>List<Manager>没有继承关系。

6.7 通配符类型

6.7.1 通配符概念

在通配符类型中,允许类型参数发生变化,例如Pair<? extends Employee>表示任何泛型Pair类型,他的我类型参数是Employee的子类。

加入要实现这个方法:

java">public static void print(Pair<Employee> p){...
}

8.6所说,不能将Pair<Manager>传递给这个方法,但是可以使用通配符解决这个问题。如:

java">public static void print(Pair<? extends Employee> p){...
}
6.7.2 通配符的超类限定

通配符限定和类型变量限定很相似,但是还有附加的功能,就是可以指定一个超类型限定,如:

? super Manager

可以限制为Manager的所有超类型。

6.7.3 无限定通配符

还可以使用根本无限顶的通配符,例如Pair<?>

带有无限定通配符的可以被任意Object对象调用原始类中的方法。

6.8 反射和泛型

6.8.1 泛型Class

Class类是泛型的,例如String.class实际就是Class<String>类的对象。

Class方法说明
T newInstance()返回无参构造器构造的一个新实例
T cast(Object obj)如果obj为null或有可能转换成类型T,则返回obj;否则抛出异常
T[] getEnumConstants()如果T是枚举类型,返回所有值组成的数组;否则返回null
Class<? super T> getSuperclass()返回这个类的超类,如果不是类或Object对象,返回null
Constructor<T> getConstructor(Class... parameterTypes)获得公共构造器
Constructor<T> getDeclaredConstructor(Class... parameterTypes)获得有给定参数类型的构造器
TypeVariable[] getTypeParameters()这个类型被声明为泛型,则获得泛型变量,否则获得长度为0的数组
Type getGenericSuperclass()获得这个类型所声明的超类的泛型类型;否则返回null
Type[] getGenericInterfaces()获得这个类所声明的接口的泛型类型;否则返回长度为0的数组
Constructor方法说明
T newInstance(Object... parameters)返回用指定参数构造的新实例
Method方法声明
TypeVariable[] getTypeParameters()获得泛型类型变量;否则返回长度0的数组
Type getGenericReturnType()获得这个方法声明的泛型返回类型
Type[] getGenericParameterTypes()获得声明的泛型参数类型;否则返回长度0数组
TypeVariable方法声明
String getName()获得这个类型变量的名字
Type[] getBounds()获得这个类型变量的子类限定,否则返回长度0数组
WildcardType方法声明
Type[] getUpperBounds()获得子类限定;否则返回长度0数组
Type[] getLowerBounds()获得超类限定;否则返回长度0数组
ParameterizedType方法声明
Type getRawType()获得原始类型
Type[] getActualTypeArguments()获得参数化类型声明的类型参数
Type getOwnerType()如果是内部类型,返回其外部类型;否则返回null
GenricArrayType方法声明
Type getGenericComponentType获得数组类型声明的泛型元素类型

http://www.ppmy.cn/embedded/28382.html

相关文章

在进行YOLOv3模型部署时,有哪些常见的硬件平台选择和它们的优缺点是什么?

YOLOv3模型部署时可以选择多种硬件平台&#xff0c;每种平台都有其特定的优缺点&#xff0c;适用于不同的应用场景。以下是一些常见的硬件平台选择及其优缺点&#xff1a; 1. GPU&#xff08;图形处理单元&#xff09;&#xff1a; - 优点&#xff1a; - 高并行处理能…

java泛型详解

简介 Java 泛型&#xff08;Generics&#xff09;是 Java 语言在 5.0 版本中引入的一个核心概念&#xff0c;用于在编译时提供更严格的类型检查&#xff0c;并支持编写可重用的代码。通过使用泛型&#xff0c;你可以在类、接口和方法中定义类型参数&#xff0c;这些类型参数在…

HTML_CSS学习:CSS选择器

一、通配选择器 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>通配选择器</title><style>* {color: #1b8335;font-size: 40px;}/*可以选中所有的HTML元素*/<…

java-stream流案例

需求 代码 Vote类 // 1. 定义一个投票类 public class Vote {private String name;private ArrayList<String> voteList;public Vote(String name, ArrayList<String> voteList) {this.name name;this.voteList voteList;}public String getName() {return nam…

VSCode 配置 Qt 开发环境

文章目录 1. 环境说明2. 配置系统环境变量 1. 环境说明 操作系统&#xff1a;Windows 11VSCode版本&#xff1a;1.88.1CMake版本&#xff1a;3.27.7Qt6版本&#xff1a;6.7.0(MinGW 11.2.0 64-bit) 2. 配置系统环境变量 自行根据自己的Qt安装路径配置 配置 MinGW 和 CMake C…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

带宽的理解-笔记

带宽的理解 带宽(频带宽度)&#xff1a;是指电磁波最高频率和最低频率的差值&#xff0c;这一段频率被称为带宽。 举例说明 人耳能听到的频率范围是20赫兹到2万赫兹。换句话说&#xff0c;人而只对20赫兹至2万赫兹的声音频率有反应&#xff0c;超出或低于这一频率范围的声音我…

springboot全局处理sql异常

springboot全局异常处理&#xff0c;不会去拦截sql异常&#xff0c;导致错误返回到前端&#xff0c;&#xff0c;将sql中的字段暴露出来&#xff0c;很危险&#xff0c;&#xff0c; 因为ExceptionHandler 只认我们注入类的名称&#xff0c;&#xff0c;而SQLException不在里面…