Kotlin泛型之 循环引用泛型(A的泛型是B的子类,B的泛型是A的子类)

IDE(编辑器)报错

循环引用泛型是我起的名字,不知道官方的名字是什么。这个问题是我在定义Android 的MVP时提出来的。具体是什么样的呢?我们看一下我的基础的MVP定义:

interface IPresenter<V> {  fun getView(): V  
}interface IView<P> {  fun getPresenter(): P  
}

这里我定义了一个View和Presenter的接口,但是实际上这两个东西现在没什么关联。

这时,我想提供一个Presenter与View的基类,在基类中实现某些通用功能,并且增加这样的约束:Presenter中拿到的V必须是View的子类,View中拿到的P必须是Presenter的子类,那我们知道,可以通过kotlin,指定上界的方式。

我们修改上边的代码:改成下边这样:

interface IPresenter<V: IView> {  fun getView(): V  
}interface IView<P: IPresenter> {  fun getPresenter(): P  
}

同时我们也很容易发现,编译器报错了:

请添加图片描述

IDE提示这个错误:One type argument expected for interface IPresenter<V>,意思是:IPresenter必须指定一个泛型类型才可以。在Kotlin中,这样的写法是直接保存的,在Java中,这种写法只是警告,所以Java中使用这种写法,“没问题”(没有IDE报错问题)。

Java 中如何实现这个定义

我们来看一下Java

interface IPresenter<V extends IView> {  V getView();  
}  interface IView<P extends IPresenter> {  P getPresenter();  
}

接着我们看IDE的提示:

请添加图片描述

出现了警告,但是不报错了,我们看一下提示是什么:
Raw use of parameterized class 'IPresenter' 意思是其实和Kotlin提示的差不多,希望你指定一个泛型类型,而不是直接使用这个类。

在Java中,为了解决这个提示,可以使用?

interface IPresenter<V extends IView<?>> {  V getView();  
}  interface IView<P extends IPresenter<?>> {  P getPresenter();  
}

当我们在后边的泛型中加入来规定一下泛型,编译器就不会提示错误和警告。

在Kotlin中如何实现类似的接口定义?

我们尝试在Kotlin的代码中实现增加上界的方式: 我们知道kotlin 的通配符 *对应着java中的?,那我们模仿一下Java的方式不就行了吗?

interface IView<P: IPresenter<*>> {  fun getPresenter(): P  
}  interface IPresenter<V: IView<*>> {  fun getView(): V  
}

结果发现又报错了:

This type parameter violates the Finite Bound Restriction

请添加图片描述

然后我又尝试了很多方式都不行。

解决方案

1、利用Kotlin与Java混合开发

既然Java中是可以定义的,那么可以通过定义一个Java类型的接口不就行了。确实可以,但是当这个定义不是接口,而是抽象类时,你需要再Java代码中写一堆方法实现、属性定义,这时你就无法体验到Kotlin代码的优势了。

定义用Java,实现用Kotlin。

2、增加Self泛型

在StackOverFlow 找到了一个解决方案,就是增加一个泛型,

interface IView<Self: IView<Self, P>, P: IPresenter<P, Self>> {  fun getPresenter(): P  
}  interface IPresenter<Self: IPresenter<Self,V>, V: IView<V, Self>> {  fun getView(): V  
}

我们通过增加一个泛型,并传递自己的方式,实现了循环引用泛型。

但是这样的代码,看起来定义变长了

当然我们为了看起来更清晰,也可以改成kotlin的另一种写法:

interface IView<Self, P> where Self: IView<Self, P>, P: IPresenter<P, Self>{  fun getPresenter(): P  
}  interface IPresenter<Self, V> where Self: IPresenter<Self,V>, V: IView<V, Self>{  fun getView(): V  
}

虽然变长了,但是定义看起来清晰了,缺点就是,增加多了一个泛型。

也可以把Self放在后边

interface IView<P, Self> where P: IPresenter<Self, P>, Self: IView<P, Self> {  fun getPresenter(): P  
}  interface IPresenter<V, Self> where Self: IPresenter<V, Self>, V: IView<Self, V>{  fun getView(): V  
}

在使用时,我们只需要把Self指定为当前类即可。

class LoginView() : IView<LoginPresenter, LoginView> {  override fun getPresenter(): LoginPresenter {  TODO("Not yet implemented")  }  
}  class LoginPresenter() : IPresenter<LoginView, LoginPresenter> {  override fun getView(): LoginView {  TODO("Not yet implemented")  }  
}

完整案例MVP

通常我们不会在接口的位置直接定义循环引用,更多的时候是在实现某一个方案时,发现有通用的功能,想把通用功能统一抽离在父类里边。

案例:登录功能,我现在有一个登录的功能,为了简单,此处只增加两种方式:手机号码&密码登录。手机号码&验证码登录。

首先看这个功能的通用逻辑部分:
1、手机号码验证。
2、登录成功之后的用户数据获取。

各自独立的部分:
密码登录:检查密码位数,用密码登录。
验证码登录:发送验证码,检查验证码,用验证码登录。

  
interface IView<P> {  fun getPresenter(): P?  
}  interface IPresenter<V> {  fun getView(): V?  
}  abstract class BaseView<P : BasePresenter<*>>() : IView<P> {  private var myPresenter: P? = null  override fun getPresenter(): P? {  if (myPresenter == null) {  myPresenter = createPresenter()  myPresenter?.setView(this)  }  return myPresenter  }  fun showLoadingView() {  // 具体的展示加载中动画的实现  }  fun hideLoadingView() {  // 具体的去除加载中动画的实现  }  fun destroyView() {  hideLoadingView()  // 其他页面销毁时操作  }  abstract fun createPresenter(): P?  
}  abstract class BasePresenter<V> : IPresenter<V> {  protected var myView: V? = null  override fun getView(): V? {  return myView  }  fun setView(view: Any?) {  this.myView = view as V?  }  }  abstract class BaseLoginView<Presenter, Self> :  BaseView<Presenter>() where Self : BaseLoginView<Presenter, Self>, Presenter : BaseLoginPresenter<Self, Presenter> {  }  abstract class BaseLoginPresenter<View, Self> :  BasePresenter<View>() where View : BaseLoginView<Self, View>, Self : BaseLoginPresenter<View, Self> {  fun checkPhone(phone: String): Boolean {  // 检查手机号具体实现  return true  }  fun onObtainTokenSuccess(token: String) {  // 登录成功具体操作  }  
}  class LoginCodeView : BaseLoginView<LoginCodePresenter, LoginCodeView>() {  override fun createPresenter(): LoginCodePresenter {  return LoginCodePresenter()  }  }  class LoginCodePresenter : BaseLoginPresenter<LoginCodeView, LoginCodePresenter>() {  fun checkCode(): Boolean {  // 检查Code 的具体代码  return true  }  
}  class LoginPasswordView : BaseLoginView<LoginPasswordPresenter, LoginPasswordView>() {  override fun createPresenter(): LoginPasswordPresenter {  return LoginPasswordPresenter()  }  }  class LoginPasswordPresenter : BaseLoginPresenter<LoginPasswordView, LoginPasswordPresenter>() {  fun checkCode(): Boolean {  // 检查Code 的具体代码  return true  }  
}

参考链接

https://stackoverflow.com/questions/46682455/how-to-solve-violation-of-finite-bound-restriction-in-kotlin

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

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

相关文章

Nodejs 第六十八章(远程桌面)

远程桌面 远程桌面&#xff08;Remote Desktop&#xff09;是一种技术&#xff0c;允许用户通过网络远程连接到另一台计算机&#xff0c;并在本地计算机上控制远程计算机的操作。通过远程桌面&#xff0c;用户可以在不同地点的计算机之间共享屏幕、键盘和鼠标&#xff0c;就像…

宝塔面板安装教程(linux)

宝塔官网地址 宝塔官网linux安装地址 针对Ubuntu系统的安装命令&#xff1a; wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh ed8484bec 安装过程中&#xff0c;中途会出现一个 Y&N ? 的选项&#xf…

OpenCV如何模板匹配

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何实现背投 下一篇 &#xff1a;OpenCV在图像中寻找轮廓 目标 在本教程中&#xff0c;您将学习如何&#xff1a; 使用 OpenCV 函数 matchTemplate()搜索图像贴片和输入图像之间…

如何下载AndroidStudio旧版本

文章目录 1. Android官方网站2. 往下滑找到历史版本归档3. 同意软件下载条款协议4. 下载旧版本Androidstudio1. Android官方网站 点击 Android官网AS下载页面 https://developer.android.google.cn/studio 进入AndroidStuido最新版下载页面,如下图: 2. 往下滑找到历史版本归…

一本书了解AI的下一个风口:AI Agent

在数字化浪潮中&#xff0c;人工智能&#xff08;AI&#xff09;已成为推动现代社会前进的强劲引擎。 从智能手机的智能助手到自动驾驶汽车的精准导航&#xff0c;AI技术的应用已经渗透到生活的方方面面。 随着技术的飞速发展&#xff0c;我们正站在一个新的转折点上&#xff…

构建本地大语言模型知识库问答系统

MaxKB 2024 年 4 月 12 日&#xff0c;1Panel 开源项目组正式对外介绍了其官方出品的开源子项目 ——MaxKB&#xff08;github.com/1Panel-dev/MaxKB&#xff09;。MaxKB 是一款基于 LLM&#xff08;Large Language Model&#xff09;大语言模型的知识库问答系统。MaxKB 的产品…

[论文笔记]GAUSSIAN ERROR LINEAR UNITS (GELUS)

引言 今天来看一下GELU的原始论文。 作者提出了GELU(Gaussian Error Linear Unit,高斯误差线性单元)非线性激活函数&#xff1a; GELU x Φ ( x ) \text{GELU} x\Phi(x) GELUxΦ(x)&#xff0c;其中 Φ ( x ) \Phi(x) Φ(x)​是标准高斯累积分布函数。与ReLU激活函数通过输入…

网盘—上传文件

本文主要讲解网盘里面关于文件操作部分的上传文件&#xff0c;具体步骤如下 目录 1、实施步骤&#xff1a; 2、代码实现 2.1、添加上传文件协议 2.2、添加上传文件槽函数 2.3、添加槽函数定义 2.4、关联上传槽函数 2.5、服务器端 2.6、在服务器端添加上传文件请求的ca…

算法学习(5)-图的遍历

目录 什么是深度和广度优先 图的深度优先遍历-城市地图 图的广度优先遍历-最少转机 什么是深度和广度优先 使用深度优先搜索来遍历这个图的过程具体是&#xff1a; 首先从一个未走到过的顶点作为起始顶点&#xff0c; 比如以1号顶点作为起点。沿1号顶点的边去尝试访问其它未…

提升编码技能:学习如何使用 C# 和 Fizzler 获取特价机票

引言 五一假期作为中国的传统节日&#xff0c;也是旅游热门的时段之一&#xff0c;特价机票往往成为人们关注的焦点。在这个数字化时代&#xff0c;利用爬虫技术获取特价机票信息已成为一种常见的策略。通过结合C#和Fizzler库&#xff0c;我们可以更加高效地实现这一目标&…

2024年---蓝桥杯网络安全赛道部分WP

一、题目名称&#xff1a;packet 1、下载附件是一个流量包 2、用wireshark分析&#xff0c;看到了一个cat flag的字样 3、追踪http数据流&#xff0c;在下面一行看到了base64编码。 4、解码之后得到flag 二、题目名称&#xff1a;cc 1、下载附件&#xff0c;打开是一个html …

Docker构建LNMP部署WordPress

前言 使用容器化技术如 Docker 可以极大地简化应用程序的部署和管理过程&#xff0c;本文将介绍如何利用 Docker 构建 LNMP 环境&#xff0c;并通过部署 WordPress 来展示这一过程。 目录 一、环境准备 1. 项目需求 2. 安装包下载 3. 服务器环境 4. 规划工作目录 5. 创…

CAPS Wizard for Mac:打字输入辅助应用

CAPS Wizard for Mac是一款专为Mac用户设计的打字输入辅助应用&#xff0c;以其简洁、高效的功能&#xff0c;为用户带来了全新的打字体验。 CAPS Wizard for Mac v5.3激活版下载 该软件能够智能预测用户的输入内容&#xff0c;实现快速切换和自动大写锁定&#xff0c;从而大大…

OmniReader Pro for Mac:强大且全面的阅读工具

OmniReader Pro for Mac是一款专为Mac用户设计的强大且全面的阅读工具&#xff0c;它集阅读、编辑、管理等多种功能于一身&#xff0c;为用户提供了卓越的阅读体验。 OmniReader Pro for Mac v2.9.5激活版下载 该软件支持多种文件格式的阅读&#xff0c;包括PDF、Word、Excel、…

pycharm配置wsl开发环境(conda)

背景 在研究qanything项目的过程中&#xff0c;为了进行二次开发&#xff0c;需要在本地搭建开发环境。然后根据文档说明发现该项目并不能直接运行在windows开发环境&#xff0c;但可以运行在wsl环境中。于是我需要先创建wsl环境并配置pycharm。 wsl环境创建 WSL是“Windows Su…

新时代写作与互动:《一本书讲透 Elasticsearch》读者群的创新之路

1、《一本书讲透 Elasticsearch》销售最近进展汇报 给大家同步一下《一本书讲透 Elasticsearch》图书的进展情况&#xff0c;本周五&#xff08;2024年4月26日&#xff09;&#xff0c;出版社编辑老师反馈图书相关销量进展&#xff1a; 预计全网销量 1000 册&#xff0c;发货量…

OpenHarmony语言基础类库【@ohos.xml (xml解析与生成)】

将XML文本转换为JavaScript对象、以及XML文件生成和解析的一系列接口。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import xml from ohos.xml; XmlSerializer XmlSerializer接口…

FPGA实现图像处理之【直方图均衡-寄存器版】

FPGA实现直方图统计 一、图像直方图统计原理 直方图的全称为灰度直方图&#xff0c;是对图像每一灰度间隔内像素个数的统计。即对一张图片中每隔二灰度值的像素数量做统计&#xff0c;然后以直方图的形式展现出来。图下的亮暗分布在直方图中就可以一目了然&#xff0c;直方图…

Spark核心名词解释与编程

Spark核心概念 名词解释 1)ClusterManager&#xff1a;在Standalone(上述安装的模式&#xff0c;也就是依托于spark集群本身)模式中即为Master&#xff08;主节点&#xff09;&#xff0c;控制整个集群&#xff0c;监控Worker。在YARN模式中为资源管理器ResourceManager(国内…

paddlehub的简单应用

1、下载安装 pip install paddlehub -i https://pypi.tuna.tsinghua.edu.cn/simple 报错&#xff1a; Collecting onnx<1.9.0 (from paddle2onnx>0.5.1->paddlehub)Using cached https://pypi.tuna.tsinghua.edu.cn/packages/73/e9/5b953497c0e36df589fc60cc6c6b35…