为什么Kotlin内联函数参数不能为null 示例详细说明当编译器无法解析if语句时

如何解决为什么Kotlin内联函数参数不能为null 示例详细说明当编译器无法解析if语句时

enter image description here

inline fun <T,R> isNullObject(value: T?,notNullBlock: (T) -> R,isNullBlock: (() -> Unit)? = null) {

if (value != null) {
    notNullBlock(value)
} else {
    if(isNullBlock != null){
        isNullBlock()
    }
}

}

我试图编写一些高阶函数来促进开发,但这是错误的

解决方法

我认为这与inline functions和传递给它的lambda如何内联有关。内联修饰符会影响函数本身以及传递给它的lambda:所有这些都将内联到调用站点中。看来Kotlin不允许使用 nullable lambda。

如果您想要Ctrl+Shift+P参数的默认值,则可以使用大括号isNullBlock

isNullBlock: () -> Unit = {}
,

Android开发者倡导者Florina Muntenescu提供了great post来说明inline的工作原理。按照所有说明,应该清楚为什么不允许可空的lambda。

简而言之:

由于使用了inline关键字,编译器将内联函数的内容复制到了调用站点,从而避免了创建新的Function对象。

这是内联关键字给我们带来的性能优势。但是,为了执行该操作,编译器必须确保始终传递lambda参数,无论该参数是否为空。当您尝试使lambda参数为可空时,编译器将无法将null lambda的内容复制到调用站点。同样,您无法执行!= null之类的比较操作,也无法使用?来包装应该内联的可选lambda,因为在编译时将不会存在lamda / function对象。下面有更多说明。

示例(详细说明)

在我的示例中,您的函数已更新,并将空lambda用作isNullBlock的默认参数:

inline fun <T,R> isNullObject(value: T?,notNullBlock: (T) -> R,isNullBlock: (() -> Unit) = {}) {
    if (value != null) {
        notNullBlock(value)
    } else {
        isNullBlock()
    }
}

这是反编译为Java的isNullObject函数的非内联版本的用法。

科特琳码

class Test {
    init {
        isNullObject(null as? Int,{
                println("notNullBlock called")
                it
            },{ println("isNullBlock called") })
        isNullObject(0,{ println("isNullBlock called") })
    }
}

反编译的Java代码

public final class Test {
   public Test() {
      TestKt.isNullObject((Integer)null,(Function1)null.INSTANCE,(Function0)null.INSTANCE);
      TestKt.isNullObject(0,(Function0)null.INSTANCE);
   }
}

如您所见,没有什么异常的事情发生(尽管,很难理解null.INSTANCE是什么)。您的isNullObject函数以Kotlin中定义的三个参数传递。

这是您的内联函数如何使用相同的Kotlin代码进行反编译。


public final class Test {
   public Test() {
      Object value$iv = (Integer)null;
      int $i$f$isNullObject = false;
      int var3 = false;
      String var4 = "isNullBlock called";
      boolean var5 = false;
      System.out.println(var4);
      int value$iv = false;
      $i$f$isNullObject = false;
      int var8 = false;
      String var9 = "notNullBlock called";
      boolean var6 = false;
      System.out.println(var9);
   }
}

对于第一个函数调用,我们立即将if (value != null)语句解析为false,而传入的notNullBlock甚至没有最终代码。在运行时,无需每次都检查该值是否为null。由于isNullObject内联了Lambda,因此不会为Lambda参数生成Function对象。这意味着没有任何可检查的可为空性。另外,这就是为什么您无法保留对内联函数的lambda / function参数的引用的原因。

Object value$iv = (Integer)null;
int $i$f$isNullObject = false;
int var3 = false;
String var4 = "isNullBlock called";
boolean var5 = false;
System.out.println(var4);

但是内联仅在编译器能够在编译时获取给定参数的值时有效。如果第一个参数不是isNullObject(null as? Int,...)isNullObject(0,...)而是函数调用-内联将无济于事!

当编译器无法解析if语句时

已添加功能-getValue()。返回可选的Int。编译器无法提前知道getValue()调用的结果,因为它只能在运行时计算。因此,内联只做一件事-将isNullObject的全部内容复制到Test类构造函数中,并为每个函数调用两次。仍然有一个好处-我们摆脱了在运行时创建的4个Function实例来保存每个lambda参数的内容。

科特琳

class Test {
    init {
        isNullObject(getValue(),{ println("isNullBlock called") })
        isNullObject(getValue(),{ println("isNullBlock called") })
    }

    fun getValue(): Int? {
        if (System.currentTimeMillis() % 2 == 0L) {
            return 0
        } else {
            return null
        }
    }
}

反编译的Java

public Test() {
      Object value$iv = this.getValue();
      int $i$f$isNullObject = false;
      int it;
      boolean var4;
      String var5;
      boolean var6;
      boolean var7;
      String var8;
      boolean var9;
      if (value$iv != null) {
         it = ((Number)value$iv).intValue();
         var4 = false;
         var5 = "notNullBlock called";
         var6 = false;
         System.out.println(var5);
      } else {
         var7 = false;
         var8 = "isNullBlock called";
         var9 = false;
         System.out.println(var8);
      }

      value$iv = this.getValue();
      $i$f$isNullObject = false;
      if (value$iv != null) {
         it = ((Number)value$iv).intValue();
         var4 = false;
         var5 = "notNullBlock called";
         var6 = false;
         System.out.println(var5);
      } else {
         var7 = false;
         var8 = "isNullBlock called";
         var9 = false;
         System.out.println(var8);
      }

   }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-