Kotlin 泛型小知识: `<T>`, `<out T>`, `<in T>` 的区别

news2024/11/25 10:30:42

在这里插入图片描述

引言

我们是不是常常在 Kotlin 的代码中看到一些奇怪的符号,比如 <out T> 或者 <T>?如果我们对这些泛型(Generics)符号还不太了解,没关系!今天我们就来聊一聊它们的区别,以及如何在实际开发中正确使用它们。😊

泛型:它是什么?为什么要用?📚

首先,让我们简单了解一下**泛型(Generics)**是什么。泛型就是一种设计模式,它让我们的代码更加通用化,能够适用于多种数据类型,而不是被限制在某一种类型上。

举个例子:

fun printItem(item: Any) {
    println(item)
}

这个 printItem 函数看起来不错,它接受 Any 类型的参数,意味着任何类型的对象都能传进来。😃

但是,Kotlin 的泛型可以让我们做得更好!假设我们希望创建一个可以存储和获取不同类型数据的容器类,我们可以使用泛型来定义它:

class Container<T>(var item: T) {
    fun getItem(): T {
        return item
    }
}

这里的 <T> 就是泛型标记,表示我们将使用一个类型 T,并且这个类型是由使用者来指定的。

那么,<T><out T> 有什么区别呢?🧐

1. <T>:协变与逆变

在 Kotlin 中,泛型类型参数是不变的(Invariant)。这意味着对于一个类 Container<T>,如果 AB 的子类型(A : B),那么 Container<A> 并不是 Container<B> 的子类型。比如:

val stringList: List<String> = listOf("Hello")
val anyList: List<Any> = stringList // 错误:类型不匹配

即使 StringAny 的子类型,List<String> 也不是 List<Any> 的子类型。这种类型的不兼容性在一些情况下会带来麻烦。

2. out:协变(Covariance)

为了让泛型类型在子类型关系中表现得更灵活,Kotlin 引入了协变和逆变的概念。

  • 协变:用 out 关键字表示,意味着泛型类型可以从一种类型安全地转换为另一种类型。
  • 协变适用于只读的情况,也就是说,我们只从中获取数据,不会修改其中的数据。

举个例子:

interface Source<out T> {
    fun nextT(): T
}

这里的 out T 表示 Source 是协变的,这意味着如果 CatAnimal 的子类型,那么 Source<Cat> 也是 Source<Animal> 的子类型。

为什么这样?因为 Source 只提供 T 类型的数据,而不修改它。这保证了类型安全。

使用 out 的场景

当我们在设计类或接口时,如果我们希望该类的泛型类型参数仅用于返回(输出)数据,而不会用于接收(输入)数据时,我们应该使用 out 关键字。🌟

fun demo(source: Source<Animal>) {
    val catSource: Source<Cat> = object : Source<Cat> {
        override fun nextT(): Cat {
            return Cat()
        }
    }

    val animalSource: Source<Animal> = catSource // 协变,安全转换
}

3. in:逆变(Contravariance)

out 相反,in 用于表示逆变(Contravariance)。in 关键字表示泛型类型参数只能用于接收(输入)数据,而不能用于返回(输出)数据。💡

逆变的应用

假设我们有一个消费者类,它只消耗数据而不产生数据:

interface Consumer<in T> {
    fun consume(item: T)
}

在这种情况下,如果 AnimalCat 的父类型,那么 Consumer<Animal>Consumer<Cat> 的子类型。逆变可以安全地传递更广泛类型的对象。

4. 不使用 inout:不变(Invariant)

当我们既需要输入又需要输出数据时,就不应该使用 inout。这样的泛型类型参数称为不变(Invariant)。

class Container<T>(var item: T) {
    fun getItem(): T = item
    fun setItem(item: T) {
        this.item = item
    }
}

这里的 Container<T> 同时用于输入和输出 T 类型的数据,因此不能使用 inout,它是不变的。

总结 📝

  • <T>:默认情况下是不变的,即 Container<A> 不是 Container<B> 的子类型。
  • <out T>:表示泛型参数是协变的,只能作为返回值(输出)使用。这种情况适用于只读取数据的场景。
  • <in T>:表示泛型参数是逆变的,只能作为参数(输入)使用。这种情况适用于只传递数据的场景。

感谢阅读!

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

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

相关文章

c语言(二叉树)

第4章 二叉树和BST 树与二叉树 基本概念 树是一种非线性结构&#xff0c;其严格的数学定义是&#xff1a;如果一组数据中除了第一个节点&#xff08;第一个节点称为根节点&#xff0c;没有直接前驱节点&#xff09;之外&#xff0c;其余任意节点有且仅有一个直接前驱&#xff…

macos 10.15 catalina xcode 下载和安装

在macos 10.15 catalina系统中, 由于系统已经不再支持,所以我们无法通过应用商店来安装xcode, 需要手动下载指定版本的 xcode 版本才能安装, catalina 支持的最新xcode版本为 Xcode v12.4 (12D4e) , 其他的新版本是无法安装在Catalina系统中的. Xcode_12.4.xip下载地址 注意,下…

安装虚拟机(VMware)教程

VMware-虚拟机使用 一、VMware 介绍二、VMware 下载三、VMware 安装1.第一步&#xff0c;点击安装的*.exe文件&#xff0c;出现以下画面2.第二步&#xff0c;点击下一步&#xff0c;到选择安装的路径&#xff0c;默认安装和自定义安装3.第三步&#xff0c;点击安装&#xff0c;…

postgresql底层Driver驱动包数据库是怎么连接,怎么发送数据,怎么设置超时

##一、建立连接 ##connectionFactory.openConnectionImpl ##底层也没什么神秘的&#xff0c;就是使用的socket通讯 ##连接&#xff0c;打开流 ##socket.connect ##获取到读写流 ##二、数据库连接池创建Connection连接 ##三、发送相关sql数据 ##发数据 ##发送查询 ##sendParse(q…

nefu暑假acm集训1 构造矩阵 个人模板+例题汇总

前言&#xff1a; 以下都是nefu暑假集训的训练题&#xff0c;我在此把我的模板和写的一些练习题汇总一下并分享出来&#xff0c;希望在能满足我复习的情况下能帮助到你。 正文&#xff1a; 模板&#xff1a; #include<bits/stdc.h> using namespace std; typedef long…

手机三网状态实时查询分享

我们演示如何使用Python对接手机号在网状态API接口 以下是详细的接口文档&#xff1a;https://www.tanshuapi.com/market/detail-123 首先&#xff0c;您需要注册并获取API密钥。假设您已经拥有API密钥&#xff0c;接下来是具体的实现步骤。 编写Python代码 以下是一个Python…

如何使用Gogs搭建自己的git服务器

最近偶然发现一款轻量级的git服务器&#xff0c;以前一直用的svn server&#xff0c;最近想搞个git服务器&#xff0c; 用gitlab资源占用太多了&#xff0c;gogs是一款轻量级git服务器&#xff0c;非常适合个人使用。 项目地址&#xff1a;GitHub - gogs/gogs: Gogs is a painl…

隔离电源不能与其他电源共地吗

隔离电源通常设计用于在电气系统中提供安全隔离&#xff0c;以避免不同部分之间的电流回路和潜在的电气干扰。隔离电源的一个关键特点是其输出端与输入端之间没有直接的电气连接&#xff0c;而是通过变压器、光电耦合器或其他隔离手段实现。 关于隔离电源与其他电源共地的问题&…

神神叨叨的OpenAI和神神叨叨的草莓

其实现在的OpenAI挺烦人的&#xff0c;和之前Ilya在的时候就不太一样了&#xff0c;Schulman也走了, 尤其Schulman走了是真的可惜&#xff0c;因为他是整个后训练&#xff0c;包括微调尤其是RLHF的真正操盘手 Altman发草莓然后又没下文&#xff0c;挺败好感的&#xff0c;大家…

Ubuntu安装交叉编译工具链(gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu)

1&#xff1a; 首先把压缩包解压出来 2&#xff1a; 我是把文件名字进行了修改&#xff0c;这个看个人需求。终端输入&#xff1a; sudo vim .bashrc在文件最末尾输入&#xff1a; export PATH$PATH:/home/the/aarch64-linux-gnu/bin export LD_LIBRARY_PATH$LD_LIBRARY_PAT…

合合信息acge模型获C-MTEB第一,文本向量化迎来新突破

前言&#xff1a; 在当今时代&#xff0c;大型语言模型以其惊人的发展速度和广泛的应用前景&#xff0c;正成为全球科技界的瞩目焦点。这些模型的强大能力&#xff0c;源自于背后默默支撑它们的Embedding技术——一种将语言转化为机器可理解的数值向量的关键技术。随着大型语言…

05:创建逻辑软件元件库

1.创建逻辑软件元件库 点击 “编辑电参数” 1.1常规设置 1.2PCB封装 1.3门 1.4管脚 1.5检查元件 点击确定 1.6点击保存 2.处理重叠问题 2.1查看处理后的显示

【Linux】:用户缓冲区

1.前言&#xff08;引出现象&#xff09; 我们看一段代码&#xff0c; 我们运行这段代码&#xff0c; 再次运行&#xff0c;并将打印结果重定向到文件log.txt中&#xff0c; 结果除了系统调用write的输出&#xff0c;其余输出都多打印一次。这是为什么呢&#xff1f;我们先…

IO进程线程 0829作业

作业 1、将消息队列发送接收端实现一遍。 send.c代码 #include <myhead.h>struct msgbuf {long mtype; /* 消息的类型*/char mtext[1000]; /* 消息的正文&#xff0c;长度不是1&#xff0c;可以自定义*/ };#define leng sizeof(struct msgbuf) - sizeof(long…

2022 年高教社杯全国大学生数学建模竞赛-C 题 古代玻璃制品的成分分析与鉴别详解+分类模型Python代码源码

前言 简单介绍一下我自己&#xff1a;博主专注建模四年&#xff0c;参与过大大小小数十来次数学建模&#xff0c;理解各类模型原理以及每种模型的建模流程和各类题目分析方法。参与过十余次数学建模大赛&#xff0c;三次美赛获得过二次M奖一次H奖&#xff0c;国赛二等奖。**提…

leetcode120. 三角形最小路径和,二维动态规划

leetcode120. 三角形最小路径和 给定一个三角形 triangle &#xff0c;找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 1 的两个结点。也就是说&#xff0c;如果正位于当前…

STM32-PWM驱动舵机——HAL库

什么是舵机&#xff1f; 舵机&#xff0c;也叫伺服电机&#xff0c;在嵌入式开发中&#xff0c;舵机作为一种常见的运动控制组件&#xff0c;具有广泛的应用。 舵机型号介绍&#xff1a; 市面上常见的舵机型号有 SG90、MG90S、MG995、MG996R 等等&#xff0c;主要是扭矩大小…

Linux--IO多路复用(select,poll,epoll)

IO多路复用——select&#xff0c;poll&#xff0c;epoll IO多路复用是一种操作系统技术&#xff0c;旨在提高系统处理多个输入输出操作的性能和资源利用率。与传统的多线程或多进程模型相比&#xff0c;IO多路复用避免了因阻塞IO而导致的资源浪费和低效率问题。它通过将多个IO…

使用matplotlib可视化dataframe:让你的数据更生动有趣

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 1. 简介 在数据分析和可视化领域&#xff0c;matplotlib是一个非常强大的工具。它可以帮助将数据以图形的形式展示出来&#xff0c;使得数据更加直观、易于理解。本文将介绍如何使用matplotlib来可视化pandas中的Da…

小q的数列(c语言)

1./描述 //小q最近迷上了各种好玩的数列&#xff0c;这天&#xff0c;他发现了一个有趣的数列&#xff0c;其递推公式如下&#xff1a; // //f[0] 0 f[1] 1; //f[i] f[i / 2] f[i % 2]; (i > 2) // //现在&#xff0c;他想考考你&#xff0c;问&#xff1a;给你一个n&am…