Java:泛型

news2024/9/29 15:26:20

文章目录

  • 1 基础概念
    • 1.1 泛型概念
    • 1.2 泛型好处
  • 2 泛型
    • 2.1 泛型类
      • 2.1.1. 泛型在父子继承关系上的表现
    • 2.2 泛型接口
      • 2.2.1 案例
    • 2.3 泛型方法
    • 2.4 泛型的通配
    • 2.5 泛型的擦除

1 基础概念

学习目标:

  • 理解泛型的概念及掌握泛型的好处

  • 泛型类、泛型接口的定义

  • 理解泛型在父子继承关系上的表现

  • 理解泛型的擦除

1.1 泛型概念

什么是泛型?

参数化类型。我们在写代码的时候, 可能很多时候我们并不能确定某一个参数的具体类型, 或者, 我们希望代码某个参数类型是灵活可变的, 我们可以先假定一种不存在的类型来代指这个参数类型, 当我们真正使用的时候再传入具体的类型。

请添加图片描述

相当于什么呢?

// 举例来说:比如我们之前定义一个变量
// int i ;
// 我们假设i = 1  --> 那i就是1
// 我们假设i = 2  --> 那i就是2

// 现在泛型来说
// 我们使用了一个符号来代替类型。比如我们使用这样一个定义  T data;
// 当我们传 T = String   那data就是String类型的
// 当我们传 T = Integer   那data就是Integer类型的

// 当我们传 T = AutoCar   那data就是AutoCar类型的
// 当我们传 T = Truck   那data就是Truck类型的

1.2 泛型好处

  1. 省去了类型强转的麻烦

    不用使用强制类型转换。就避免了类型强转问题。
    
  2. 将运行期遇到的问题转移到了编译期

    没有泛型之前,编译器是不会检测集合容器中元素的数据类型的,因为它们全部都是Object。使用泛型后,能让编译器在编译的时候借助传入的类型参数(实参)检查对集合容器的插入,获取等操作是否合法。
    

问题越往后才发现,造成的问题也越难以解决,或者说影响越大。

2 泛型

  • 泛型类: 泛型定义在类上。
  • 泛型接口:泛型定义在接口上
  • 泛型方法:泛型定义在方法上
  • 泛型通配: 了解即可
  • 泛型擦除:重要,要记住

泛型是在Java 5中被引入的。在Java 5之前,Java的类和方法只能通过Object来实现泛化,这样的代码存在许多问题,如类型转换错误、编译时类型检查缺失等等,限制了代码的可读性、可维护性和安全性。

引入泛型机制后,Java可以在编译时进行更严格的类型检查,使得代码更加健壮、可读性更强,并且避免了许多运行时类型转换错误的问题。

2.1 泛型类

泛型类是一种可以在定义类时使用类型参数来表示类中使用的类型的类。在Java中,泛型类可以用于定义一些通用的数据结构或算法,以便能够适应不同类型的数据。

// 泛型类:  所谓泛型类,  就是把泛型'定义在'类上
// 定义的方法  类名<泛型类型1, 泛型类型2, ...>
格式:  class 类名<泛型类型1,…>{
}

注意1:泛型的使用时候写法

// JDK1.7的写法。泛型的写法1 前面写类型,后面直接写 <>
User1<String> user1 = new User1<>();
String data = user1.data;

// JDK1.5 的时候,泛型刚刚出来时候的写法:
// 泛型的写法2 User2<类型> 变量名 = new User2<类型>();
User1<Integer> user11 = new User1<Integer>();
Integer data1 = user11.data;

注意2:默认类型

定义了泛型,但是未写在<>中,会将其直接当做Object使用。

User1 user1 = new User1();

// 如果不使用 <> 来指定类型,这时候T是个什么类型呢?
// 是默认类型,Object
Object data = user1.data;

注意3: 泛型类可以定义多个泛型

  • 可以定义多个泛型,但不建议超过两个。我们可以在一个泛型类上, 定义多个泛型, 但是建议不要超过两个(并不是语法限制)
  • 定义多个泛型,使用时,要么全部指定,要么全部不指定。当我们给一个泛型类定义多个泛型的时候, 使用的时候, 传泛型就要指明类型, 或者全不指明默认Object
User2<String, Integer> user2 = new User2<>("zs", 18);
// User2<String> user3 = new User2<>("zs", 18); // 报错,必须全部指定泛型的类型,或者全部不指定。

class User2 <T, E>{
    T name;
    E age;
}

注意4: 定义了泛型不使用: 允许

// 定义了一个K, 但是我们没有使用
class User3 <T, E, K> {
    T name;
    E age;
}

注意5: 泛型标识符

// 我们会假定一种不存在的类型来代替这个参数类型,等我们真正使用的时候再传入具体的类型。
// int i; 这个i是变量。变量名
// T data;  这个T就是标识符。
// 使用单个大写字母。比如 E T K V
// E element; T type; K key; V value

// 这是一些规范。如果不按照这个规范,写代码也可以跑,但是出问题的风险比较大。
// 切记,不要使用String这种东西作为泛型,极容易认错

注意6: 泛型必须使用引用类型

   User4<int> user1 = new User4<>(); // 报错: 泛型必须使用引用类型
   User4<Integer> user2 = new User4<>();

注意7: 泛型类, 定义了泛型之后, 泛型的作用域

  • 泛型类定义泛型的作用域: 在自己的类上,或者类中。
  • 类上:类的定义这行,可以使用泛型。class Son<T> extends Father
  • 类中:代表类体包含内容,包括内部类,可以使用泛型
class Father1{
    // 用不了T, 因为子类定义 
}

class Son<T> extends Father1{ 
    T t;
    class SonInner{     
        T aInnerT;   
    }
}

class GrandSon extends Son{
   // 用不了T, 因为父类定义 
}

2.1.1. 泛型在父子继承关系上的表现

public class Demo1 {
  public static void main(String[] args) {
    Father<Integer> f = new Father<>();
    Integer ft = f.ft;

    // Son1 定义时没有指定Father泛型的类型,所以默认为Object
    Son1 son1 = new Son1();
    Object ft1 = son1.ft;

    // Son2 定义时,未指定泛型,指定了 Father泛型为String,所以ft为String
    Son2 son2 = new Son2();
    String ft2 = son2.ft;

    //Son3 定义时,指定泛型E,指定了 Father泛型为Integer,所以ft为Integer
    Son3<String> son3 = new Son3<>();
    Integer ft3 = son3.ft;

    // Son4 定义时,指定泛型E,指定了 Father泛型为E,所以ft类型和子类一致
    Son4<Integer> son4 = new Son4<>();
    Integer ft4 = son4.ft;
    Son4<String> son41 = new Son4<>();
    String ft41 = son41.ft;

    // Son5 指定T。 与符号无关
    Son5<String> son5 = new Son5<>();
    String ft5 = son5.ft;
  }
}

class Father <T> {
    T ft;
}
class Son1 extends  Father{ }
class Son2 extends Father<String>{}
// 这个前面的E叫做定义了一个泛型E 
class Son3<E> extends Father<Integer>{}

// 这里只是看起来好像是Integer。其实是定义了一个泛型叫做Integer。它和 java.lang.Integer有区别。
// class Son3<Integer> extends Father<Integer>{}
class Son4<E> extends Father<E>{}
class Son5<T> extends Father<T>{}

背景: 如果父类有泛型,子类情况如下:

  • 如果继承时,未指定父类泛型,则为默认类型。Object

    • class Son1 extends Father{}
      
      Son1 son1 = new Son1();
      Object object = son1.ft;
      
  • 如果继承时,指定了父类类型,则为指定类型,无论子类定义泛型与否。

    • class Son2 extends Father<String>{}  --》 父类变量类型为String
      class Son3<E> extends Father<Integer>{} --》 父类变量类型为Integer
      
  • 如果继承时,传入了子类指定的泛型,则父类与子类变量类型一致

    • class Son4<E> extends Father<E>{}  // 等到使用Son4的时候,指定什么类型,就是什么类型
      class Son5<T> extends Father<T>{}
      
      // 要注意bug
      
      // 前面一个 <Integer> 叫泛型的定义,相当于我定义了一个符号
      // 后面叫使用
      class Son51<Integer> extends Father<Integer>{}
      

2.2 泛型接口

泛型接口是指在声明接口的时候使用泛型参数,以便在实现接口时指定具体的类型。这样可以使接口更加灵活和通用,可以适应不同类型的数据结构或对象。

在泛型接口中,泛型参数可以用在接口中的方法、常量、嵌套类等地方。例如:

public interface List<T> {
    void add(T element);
    T get(int index);
    int size();
}

在上面的例子中,泛型参数T可以用于add方法的参数类型和get方法的返回类型。

泛型接口使得Java中的容器类更加通用和灵活,可以适应不同类型的数据结构和对象。

// 所谓泛型接口, 就是把泛型定义在接口上
格式:  interface 接口名<泛型类型1>
// 标识符。   T  E  
// 能不能用基础类型。
// 能不能定义多个泛型。 
// 定义了能不能不使用?  
    
// 泛型标识符。   单个大写字母。 
// 格式: interface 接口名<泛型类型1…>
interface Player<T> {
    T play(T t);
}

// 如果实现时候,不指定类型,默认为Object
class YoungPlayer implements Player{
    @Override
    public Object play(Object data1) {
        return null;
    }
}

// 如果实现时,指定为什么类型,则为什么类型
class ChildPlayer implements Player<Integer>{

    @Override
    public Integer play(Integer data1) {
        return null;
    }
}

// 如果子类也有泛型,则与子类一致
// 等到这个子类,被创建的时候,才会有具体的类型。 
class OldPlayer<E> implements Player<E>{

    @Override
    public E play(E data1) {
        return null;
    }
}

泛型接口类型在什么时候确定?

子类实现该接口时候,或者子接口继承该接口时。需要指定类型

interface Player<T> {
    T play(T t);
} 
  
// 1.子类实现该接口,没有指定泛型。		--> 接口中泛型为Object
//  class ChildrenPlayer implements Player {}
//  	 public Object play(Object o) {}
  
// 2.子类实现该接口,指定了泛型,给接口指定了类型  --> 接口中泛型为指定的类型
//  class YoungPlayer implements Player<String> {}
//  	 public String play(String s) {}

// 3.子类实现该接口,指定了泛型,且符号一致  --> 接口中为指定的泛型
//  class OldPlayer<E> implements Player<E>{}
//	public E play(E e) {}

// 1.子接口继承该接口时候
与以上行为一致。

2.2.1 案例

// 转换器接口,从一个类型转化到另一个类型
// 转换器,是讲一个类型转到另外一个类型,这时候类型肯定不能写死。  所以需要泛型。  f泛型有两个
// 定义这样的接口,有什么好处?   通用。 
// 一个规范。 只要见到这个东西,我就知道它是转换,把一个类型,转换到另外一个类型。 
public interface Converter<T, R> {
    R convert(T t);
}

// 从字符串转化为时间类型 我们约定字符串的格式为 yyyy-MM-dd。即这种类型 2022-11-01
public class String2DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {

        Date parse = null;
        try {
            parse = new SimpleDateFormat("yyyy-MM-dd").parse(s);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        return parse;
    }
}

// 还可以写从String类型的转化为Integer
// 从Long 类型转化为 Date类型。

2.3 泛型方法

// 所谓泛型方法, 把泛型定义在方法上
格式:  <泛型类型> 返回类型 方法名(泛型类型)
public class Demo1 {
    public static void main(String[] args) {

        A a = new A();
        Integer t = a.getT(18);
        String zs = a.getT("zs");
        
    }
}
//格式:  <泛型类型> 返回类型 方法名(泛型类型 变量名)
class A{
    public <T> T getT(T t){
        return t;
    }
}

注意事项:

// 1.方法上没有定义泛型,只是使用了泛型,不叫泛型方法。
//比如
class Player<T>{
  T play(T t){
      System.out.println(t);
      return t;
  }
}

2.4 泛型的通配

看源码,能明白含义即可。

请添加图片描述

泛型不允许协变, 又想产生类似协变的效果, 又不想引入协变带来的问题(类型不匹配问题)

0.协变和逆变

Integer是Number的子类。
所以我们可以使用这种形式。 Number number = new Integer(10);			父类引用指向子类对象
那这个有父子继承关系吗  User<Number>  User<Integer>
 		可以这样使用吗?		User<Integer> user1 = new User<>("zs", 18);
 		 User<Number> user2 = user1;
 结论: 不行。因为  User<Number> 和 User<Integer> 不是父子继承关系。
 这个操作叫做协变。

协变就是,允许接收该类及该类的子类。

逆变就是,允许接收该类及该类的父类。

// 数组是允许协变的。协变的问题。类型不匹配问题
Animal[] animals = new Cat[10];

animals[0] = new Cat();
animals[1] = new Cat();
// 编译期没有问题。  但是运行期有问题。
animals[2] = new Dog();

泛型不允许协变,也就是

User<Animal> user = new User<Cat>();

泛型中,弄出来了几个通配,来让自己可以产生协变的效果。

泛型的通配

① 泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类了
② ? extends E
向下限定,E及其子类
③ ? super E
向上限定,E及其父类

1.任意类型

class User<T> {
  String name;
  T data;
  // getter & setter & conctructor
}
// 我们想提供一个方法,打印User对象,方法的签名是以下
// 如果是打印Integer的,可以是以下的
public void print(User<Integer> user) {
  System.out.println(user.getName() + "--" + user.getData());
}
  
// 如果是String类型的,则以上方法用不了
// 可以使用这个类型吗? 也不允许,因为泛型不允许协变
public void print(User<Object> user) {
  
// 可以使用以下类型来接收。?代表任意类型
public void print(User<?> user) {

2.向下限定

? extends E

只允许接收该类及该类子类。

// 允许接收 Number 及Number的子类
public double compute(User<? extends Number> user) {
  Number data = user.getData();
  return data.doubleValue() + 1;
}

User<Integer> user = new User<>("zs", 18);
User<Double> user2 = new User<>("zs", 18.0);

double val = genericsExtends2.compute(user);
double val2 = genericsExtends2.compute(user2);
System.out.println(val);
System.out.println(val2);

3.向上限定

? super E

只允许接收该类及其父类。

2.5 泛型的擦除

Java中的泛型并不是真的泛型, Java的泛型只存在于编译之前, 当Java中的泛型编译之后, 会把泛型编译成Object以及类型强转

为什么使用泛型,因为泛型不涉及到强制类型转换,效率高一些。 对不对?

效率没有区别。

使用的时候,安全性有区别。 jdk帮你做的。 更安全点。

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2088011.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习课程学习周报十

机器学习课程学习周报十 文章目录 机器学习课程学习周报十摘要Abstract一、机器学习部分1.1 生成对抗网络1.2 生成器与辨别器的训练过程1.3 信息论1.3.1 信息量1.3.2 熵1.3.3 交叉熵1.3.4 相对熵/KL散度1.3.5 交叉熵损失函数1.3.6 JS散度 1.4 GAN的理论介绍 总结 摘要 本周学习…

知识竞赛活动中的一些新颖特殊的赛制

以下知识竞赛海活动一些特殊新颖的竞赛规则&#xff0c;可以作为特殊情况下的参考。 &#xff08;一&#xff09;争分夺秒 答题选手&#xff1a;各队1号选手。1和2号队、3和4号队、5和6号队、7和8号队各为一组。 答题步骤&#xff1a;1号队和2号队1号选手同时离开座位&#x…

企业如何防止内部人员泄密?(5种方法详细说明)

企业内部信息泄密问题已经成为许多企业的严重威胁。随着数字化办公的普及&#xff0c;企业信息泄密的风险越来越高。内部人员泄密问题更是防不胜防&#xff0c;因此企业必须采取有效的措施来防止内部人员泄密。以下是五种可以帮助企业防止内部人员泄密的方法&#xff1a; 1. 使…

【412】【统计近似相等数对 I】

我的思路&#xff1a; 两层循环找数组两个数 然后进行1次过滤&#xff0c;如果数字相同直接下一组 不相同的话就要进行2次过滤 方便处理&#xff0c;转移到str格式 change函数用于比较两个输入的字符串是否相同 change中使用两层循环暴力调用两位数位进行交换比较&#xf…

SpringBoot 新手入门(实操)

Spring Boot 是一个开源框架&#xff0c;旨在简化基于 Spring 的 Java 应用程序的开发。它通过提供一系列默认配置和约定大于配置的理念&#xff0c;让开发者可以更快速地创建和部署应用。以下是一个 Spring Boot 新手入门的实操指南&#xff0c;帮助你从零开始创建一个简单的 …

Gitee上传项目(从0开始)

1.默认你Git已经下载好的情况下。 下载好的两种显示&#xff1a; 1.右击桌面显示这个 2.如果没有情况1出现&#xff0c;需要自己去创建快捷方式 2.去网站创建仓库 网站参考&#xff1a;yanyongzhitest/java_web - 码云 - 开源中国 (gitee.com) 新建仓库&#xff1a; 仓库名…

科研绘图系列:R语言基因PPI互作网络图(PPI network plot )

介绍 基因的PPT互作网络图。 加载R包 导入所需要的R包,在导入前需要用户自己安装。 library("tidyverse") library("magrittr") library("here") library("janitor") library("ggpubr") library("ComplexHeatmap&…

js函数方法apply,bind,call,手写new操作符

函数方法 函数方法可以用来改变函数的this指向&#xff0c;对于内置的标准函数来说&#xff0c;改变this就相当于改变了函数的作用目标&#xff1b;比如说&#xff0c;对于一个对象的方法toString()&#xff0c;可以将它的使用目标修改成指定的参数&#xff0c; 这里原本是对o…

大语言模型数据增强与模型蒸馏解决方案

背景 在人工智能和自然语言处理领域&#xff0c;大语言模型通过训练数百亿甚至上千亿参数&#xff0c;实现了出色的文本生成、翻译、总结等任务。然而&#xff0c;这些模型的训练和推理过程需要大量的计算资源&#xff0c;使得它们的实际开发应用成本非常高&#xff1b;其次&a…

Android经典实战之OkDownload:一个经典强大的文件下载开源库,支持断点续传

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 OkDownload 是一个为 Android 平台设计的开源下载框架&#xff0c;它支持多线程下载、多任务处理、断点续传等功能&#xff0c;并且具有可靠性、…

8款好用的电脑监控软件推荐?(一口气了解8款!)赶紧Get吧!

电脑监控软件成为了企业和个人管理电脑、提高工作效率、保护信息安全的重要工具。 这些软件不仅能够实时监控电脑的使用情况&#xff0c;还能帮助管理者制定合理的工作计划&#xff0c;预防潜在的安全风险。 本文将为您详细介绍八款功能强大、易于使用的电脑监控软件&#xff…

stm32之软件I2C读写MPU6050陀螺仪、加速度传感器应用案例

系列文章目录 1. stm32之I2C通信协议 文章目录 系列文章目录前言一、电路接线图二、应用案例代码三、应用案例分析3.1 I2C通信模块3.2 MPU6050模块 前言 提示&#xff1a;本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记&#xff0c;旨在学习记录&#xff0c…

空间计量 | 空间杜宾误差模型SDEM

空间计量研究中&#xff0c;空间杜宾误差模型&#xff0c;其考虑两项&#xff0c;分别是自变量X的空间滞后作用&#xff0c;以及误差扰动项的空间滞后作用&#xff0c;其数学模型公式如下&#xff1a; y βk * x θk * Wx u, u λ * Wu &#xff08;其中βk表示X的回归系…

AI学习记录 - 线性代数(3Blue1Brown)

一天更新一点点&#xff0c;只更新重点内容&#xff0c;一句话定义&#xff0c;简单的定义&#xff0c;避免脑子及记太多 向量的加法就是一种趋势运动 向量的延长缩短&#xff0c;就是分量的延长缩短 基向量就是在平面或者任意维度空间随便定义的一个向量 多个基向量的组合可…

每天分享一个FPGA开源代码(1)- spi

1、SPI总线进行通信的结构 SPI总线主要包括四根关键信号线&#xff1a; &#xff08;1&#xff09;SCK (Serial Clock) 串行时钟线&#xff0c;由主设备产生&#xff0c;控制数据传输的速率和时机。 &#xff08;2&#xff09;MOSI (Master Out Slave In) 主设备数据输出线…

指针的一些细节补充———C语言

野指针&#xff1a; 1.未初始化的指针&#xff1a; eg&#xff1a; int *p; // 未初始化的指针 *p 5; // 未定义行为&#xff0c;p 是野指针 ————————————————————————————————————————————————————————…

记Codes 开源研发项目管理平台——管理系统颠覆性创新实现之事件驱动+信息流

引言 市面上所有管理系统&#xff0c;数据都不是以推流的方式展现到前端&#xff0c;有新数据产生需主动刷新页面才能看到&#xff0c;也就是“人找事”&#xff1b;而不是主动推送的“事找人”&#xff0c;Codes 敢为人先&#xff0c;采用事件驱动信息流实现“事找人”。 1、…

一起学习LeetCode热题100道(64/100)

64.搜索二维矩阵(学习) 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。 每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&am…

Java Excel转PDF(免费)

目前市面上 Excel 转 PDF 的组件较多&#xff1a; 收费&#xff1a;aspose、GcExcel、spire开源&#xff1a;jacob、itextpdf 其中收费的组件封装得比较好&#xff0c;代码简洁&#xff0c;转换的效果也很好&#xff0c;但收费也高得离谱&#xff1a; 为了成本考虑&#xff…

共享单车|基于SprinBoot+vue的共享单车数据储存系统(源码+数据库+文档)

共享单车数据储存系统 基于SprinBootvue的共享单车数据储存系统 一、前言 二、系统设计 三、系统功能设计 系统登录注册实现 管理员模块实现 用户模块实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介…