深入理解Java泛型

农民  论坛元老 | 2025-4-26 17:02:48 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1724|帖子 1724|积分 5172

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
未完待续
一、引言

泛型(Generics)和面向对象、函数式编程一样,也是一种程序设计的范式,泛型允许程序员在定义类、接口和方法时利用引用类型的类型形参代表一些以后才能确定下来的类型,在声明变量、创建对象、调用方法时像调用函数传参一样将详细类型作为实参传入来动态指明类型。
Java的泛型,是在jdk1.5中引入的一个特性,最主要应用是在jdk1.5的新集合框架中。作为Java语法层面的东西,本博客原本不打算介绍,但考虑到泛型理解和利用起来有肯定的难度,应用的还很普遍,再加上本身工作多年好像也没有能够完全理解和机动的运用泛型,因此还是决定看一些相关的册本中与泛型有关的内容,并用一些篇幅总结放学习成果,介绍下我理解的泛型。
二、泛型类(接口)

2.1 创建泛型类

先来看两个类
  1. public class StringPrinter {
  2.     private String thingsToPrint;
  3.     public StringPrinter() {
  4.     }
  5.     public StringPrinter(String thing) {
  6.         thingsToPrint = thing;
  7.     }
  8.     public void setThingsToPrint(String thing) {
  9.         thingsToPrint = thing;
  10.     }
  11.     public void print() {
  12.         System.out.println(thingsToPrint);
  13.     }
  14. }
复制代码
  1. public class IntegerPrinter {
  2.     private String thingsToPrint;
  3.     public IntegerPrinter() {
  4.     }
  5.     public IntegerPrinter(String thing) {
  6.         thingsToPrint = thing;
  7.     }
  8.     public void setThingsToPrint(String thing) {
  9.         thingsToPrint = thing;
  10.     }
  11.     public void print() {
  12.         System.out.println(thingsToPrint);
  13.     }
  14. }
复制代码
两个类的作用相当,都是将传进来的参数进行打印,类的功能几乎完全相同,唯一的不同是参数的类型不一样,如果要为很多类型实现这个打印功能,就会编写很多的Printer类,如果要实现一个类统一实现这个功能,就可以采用泛型。
先来讲讲泛型语法,泛型用一个“菱形”声明,中是类型形参列表,如有多个类型形参,利用英文逗号,隔开。
下面程序定义了一个带有泛型声明的Printer类,有一个类型形参T(Type),声明了类的泛型参数后,就可以在类内部利用此泛型参数,构造函数名仍旧是类名本身不需要加泛型
  1. public class Printer<T> {
  2.     private T thingsToPrint;
  3.     public Printer() {
  4.         
  5.     }
  6.     public void setThingsToPrint(T thing) {
  7.         thingsToPrint = thing;
  8.     }
  9.     public Printer(T thing) {
  10.         thingsToPrint = thing;
  11.     }
  12.     public void print() {
  13.         System.out.println(thingsToPrint);
  14.     }
  15. }
复制代码
Java还能在定义类型参数时设置限定条件,如下例定义了一个NumberPrinter类,通过extends指定T的类型上限只能是Number。
⚠️留意:类型参数和第四章提到的类型通配符是不一样的,类型参数上的限定不能用super关键字,因为会造成不确定,利用extends指定T的类型上限,编译器至少知道T是个Number,如果是super关键字,编译器根本不知道T有哪些属性和方法。
  1. public class NumberPrinter<T extends Number> {
  2.     private T thingsToPrint;
  3.     public NumberPrinter() {
  4.     }
  5.     public void setThingsToPrint(T thing) {
  6.         thingsToPrint = thing;
  7.     }
  8.     public NumberPrinter(T thing) {
  9.         thingsToPrint = thing;
  10.     }
  11.     public void print() {
  12.         System.out.println(thingsToPrint);
  13.     }
  14.     public T get() {
  15.         return thingsToPrint;
  16.     }
  17. }
复制代码
还可以设置多个限定条件,extends后面只能有一个类但是可以有多个接口:
  1. public class NumberPrinter<T extends Number & Comparable<T>> {
  2.     private T thingsToPrint;
  3. }
复制代码
创建泛型接口同理,例如jdk中的List实际上就是一个接口。
  1. public interface List<E> extends Collection<E> {
  2.    
  3. }
复制代码
并非任何类都能声明为泛型类,Java规定:异常类(java.lang.Throwable)不得带有泛型
  1. public class MyException<T> extends Exception { //编译出错❌,Generic class may not extend 'java.lang.Throwable'
  2.     T msg;
  3. }
复制代码
  1. public class MyException<T> extends RuntimeException { //编译出错❌,Generic class may not extend 'java.lang.Throwable'
  2.     T msg;
  3. }
复制代码
  1. public class MyException<T> extends Throwable { //编译出错❌,Generic class may not extend 'java.lang.Throwable'
  2.     T msg;
  3. }
复制代码
2.2 实例化泛型类

利用泛型类创建对象时就可以为类型形参T传入详细类型,就可以天生类似Printer,Printer的类型
  1. public static void main(String[] args) {
  2.     // 构造器T形参是String,只能用String初始化
  3.     Printer<String> printer1 = new Printer<String>("apple");
  4.     printer1.print(); //apple
  5.     // 构造器T形参是Double,只能用Double初始化
  6.     Printer<Double> printer2 = new Printer<Double>(3.8);
  7.     printer2.print(); //3.8
  8. }
复制代码
jdk1.7以后,支持泛型类型推断,可以简写为:
  1. Printer<String> printer1 = new Printer<>("apple");
  2. Printer<Double> printer2 = new Printer<>(3.8);
复制代码
如不指定类型实参默认为Object类型,因为所有引用类型都能被Object代表,int、double、char等基本数据类型不能被Object代表,这就是类型实参必须是引用类型的原因,不外留意如果定义类型形参时通过entends指定了上限例如NumberPrinter,则不传递类型实参时默认为上限类型Number
  1. public static void main(String[] args) {
  2.     Printer printer1 = new Printer("apple");
  3.     printer1 = new Printer(12);
  4.     printer1 = new Printer(new Date());
  5.     NumberPrinter numberPrinter1 = new NumberPrinter(5);
  6.     NumberPrinter numberPrinter2 = new NumberPrinter(5.8);
  7.     NumberPrinter numberPrinter3 = new NumberPrinter(""); //编译出错❌
  8. }
复制代码
2.3 派生泛型类

派生该类时,需要指定类型实参
  1. public class HPPrinter extends Printer<Integer> {
  2. }
复制代码
通过entends指定了上限的类型需不凌驾上限类型,以下同理
  1. public class SuperNumberPrinter extends NumberPrinter<Double> {
  2. }
  3. public class SuperNumberPrinter extends NumberPrinter<Date> { //编译出错❌
  4. }
复制代码
如不利用泛型,不指定类型实参,则泛型转换为Object类型或上限类型
  1. public class HPPrinter extends Printer {
  2.     public static void main(String[] args) {
  3.         HPPrinter hpPrinter = new HPPrinter();
  4.         hpPrinter.setThingsToPrint(new Object());
  5.         hpPrinter.setThingsToPrint("hello");
  6.         hpPrinter.setThingsToPrint(12);
  7.     }
  8. }
复制代码
还可以子类和父类声明同一个类型形参,子类中也不确定详细的类型,需要子类被实例化时将类型间接传递给父类,同时子类还可以一同定义本身的泛型
  1. public class HPPrinter extends Printer<Integer> {
  2. }
复制代码
  1. public class HPPrinter extends Printer<Integer> {
  2. }
复制代码
子类确定父类泛型类型的同时,又可以有本身的泛型
  1. public class HPPrinter extends Printer<Integer> {
  2. }
复制代码
利用泛型又不指定类型的写法是错误的
  1. public class HPPrinter extends Printer<T> { //编译出错❌
  2. }
复制代码
三、泛型方法和泛型构造器

偶然候,在类和接口上不需定义类型形参,只是详细方法中的某个类型不确定,需要在方法上面定义类型形参,这个也是支持的,jdk1.5提供了对于泛型方法的支持。
3.1 泛型方法

声明方法时,在返回值前指明泛型的类型形参列表,类型形参仅作用于方法内,这个方法就声明为了泛型方法。类型形参可以出现在参数和返回值中,调用方法时指定详细类型。泛型方法可以根据需要声明为静态。任何类中都可以存在泛型方法,而不是只有泛型类中才能声明泛型方法。
<blockquote>

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农民

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表