功能接口:Kotlin中的自我厌恶

2023-10-18 16:50

本文主要是介绍功能接口:Kotlin中的自我厌恶,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

This post is a copy from previous posts on Medium (initial, follow-up) But since I'm planning on deleting my Medium account I moved them here.

Kotlin是一种很棒的编程语言。 经过大约12年的Java编程工作,与Kotlin一起工作了多年之后,感觉就像戴上眼镜一样:有太多的爱。

但是,就像每一个恋爱关系一样,您在生活的晚些时候只会发现一些怪癖。 在将越来越多的Java代码迁移到Kotlin代码之后,我注意到了一些比较奇怪且坦率的令人讨厌的地方。

就是这样科特林处理功能接口。

Java 7: a blast from the past

让我们回到没有lambda的世界。 太冗长了!

interface JavaInterface {String doSomething(Item item);
}String delegateWork(JavaInterface f) {return f.doSomething(item);
}void doWork() {delegateWork(new JavaInterface() {@Overridepublic String doSomething(Item item) {return "Item = " + item;}});
}

Java 8: Lambdas to the rescue!

最终,Java 8为我们提供了Lambdas,我们可以摆脱很多代码,专注于重要的事情。 同样,我们也不必为每个简单的函数编写自己的函数接口,而只需使用oracle提供的某些函数即可,例如:java.util.function.Function<T, R>

@FunctionalInterface
interface JavaInterface {String doSomething(Item item);
}String delegateWork(JavaInterface f) {return f.doSomething(item);
}String delegateOtherWork(Function<Item, String> f) {return f.apply(item);
}void doWork() {delegateWork(item -> "Item = " + item);delegateOtherWork(item -> "Item = " + item);
}

一切都很好,直到您意识到即使您现在拥有函数类型,它们仍不是该语言的一等公民。 要证明吗? 猜猜Java中必须引入多少个“函数类型”? 一? 三? 五?

43!

Don't believe me, see for yourself: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

And if that's not enough for you, add jOOL to the mix and you have access to 35 more: https://github.com/jOOQ/jOOL/tree/master/jOOL/src/main/java/org/jooq/lambda/function
Because who wouldn't love coming across a method signature that looks like this:

Function5<String, String, String, String, String, Tuple3<String, String, String>> higherOrder(Function12<String, Integer, String, Object, Object, Object, BiFunction<String, Integer, String>, String, Integer, Long, String, Double, Optional<Tuple2<String, String>>>)

😜旁注:jOOL实际上是一个非常简洁的库,值得一试。

Kotlin help us!

现在,将Kotlin添加到混合中。 在科特林,职能是一等公民。 因此,无需记住数十种稍有不同的功能类型。 您只需要记住Kotlin的函数类型语法:

(Parameter1Type, Parameter2Type, ParameterNType) -> ReturnType

就是这样,仅此而已。

Trouble in paradise

好吧,我们为什么在这里,怎么了?

如前所述,随着我将越来越多的代码从Java迁移到Kotlin。 使用自定义功能接口时遇到一些问题。 因为有时候您想要那种额外的描述性。

回到我们的Java 8示例

@FunctionalInterface
interface JavaInterface {String doSomething(Item item);
}class JavaComponent {private Item item = new Item();String delegateWork(JavaInterface f) {return f.doSomething(item);}String delegateOtherWork(Function<Item, String> f) {return f.apply(item);}
}

现在让我们从Kotlin代码中使用它

delegateWork { "Print $it" }
delegateOtherWork { "Print $it" }

很好,这很棒,正好符合我们的期望! 好吧,现在让我们迁移一下Java组件上科特林。 请注意,我们已经更改了java.util.function.Function<Item, String>到Kotlin函数类型(Item) -> String

class KotlinComponent(private val item: Item = Item()) {fun delegateWork(f: JavaInterface): String {return f.doSomething(item)}fun delegateOtherWork(f: (Item) -> String): String {return f.invoke(item)}
}

让我们看看使用Java代码中的这些高阶函数会发生什么。

delegateWork(item -> "Print: " + item);
delegateOtherWork(item -> "Print: " + item);

没有什么与众不同的,我们可以对两种方法使用相同的lambda。 让我们看看当我们完成Kotlin的预期时会发生什么:

delegateWork { "Print $it" }Error: Kotlin: Type mismatch: inferred type is () -> String but JavaInterface was expected

What happened? It seems the compiler can't figure out that the signature of the lambda is the same as the functional interface method. https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions

因此,我们必须明确地说出我们的期望:

delegateWork(JavaInterface { "Print $it" })

我认为这很令人失望,但还算不错。 现在让我们看看将接口迁移到Kotlin时会发生什么:

interface KotlinInterface {fun doSomething(item: Item): String
}class KotlinComponent(private val item: Item = Item()) {fun delegateWork(f: KotlinInterface): String {return f.doSomething(item)}fun delegateOtherWork(f: (Item) -> String): String {return f.invoke(item)}
}

当我们使用Kotlin组件从Java类开始,正如预期的一样,lambda保持完全相同。 如果我们从Kotlin代码中使用它,该怎么办:

delegateWork { "Print $it" }Error: Kotlin: Type mismatch: inferred type is () -> String but KotlinInterface was expected

看来SAM转换再次失败。 现在,如果我们像以前一样明确提及接口,该怎么办?

delegateWork(KotlinInterface { "Print $it" })Error: Kotlin: Interface KotlinInterface does not have constructors

这也没有帮助。 我们需要创建一个匿名对象以使其工作:

delegateWork(object : KotlinInterface {override fun doSomething(item: Item): String {return "Print $item"}
})

Yikes! This feels like working with Java 7 all over again. Sadly this is because Kotlin doesn't yet support SAM conversion for Kotlin interfaces so we have to create this anonymous object. See also:
https://youtrack.jetbrains.com/issue/KT-7770
https://stackoverflow.com/a/43737962/611032

Alias time!

那么,如何避免使用这些冗长的匿名对象,而仍然为该函数使用自定义名称? 我们使用类型别名:

/**
 * Very helpful comment.
 */
typealias KotlinFunctionAlias = (Item) -> Stringfun delegateAliasWork(f: KotlinFunctionAlias): String {return f.invoke(item)
}

因此,现在我们可以按期望的方式传递lambda了,我们仍然可以从函数的自定义名称中受益。

delegateAliasWork { "Print $it" }

这样一切就好了,案件结案了,该回家了。 不幸的是不完全是。

Lost in translation

类型别名的一个小问题是,虽然您可以命名函数类型,但不能命名方法名称:

val iface: JavaInterface = JavaInterface { "Print $it" }
iface.doSomething(item)val alias: KotlinFunctionalAlias = { item -> "Print $item" }
alias.invoke(item)
alias(item)

为类型别名和变量选择好名字可以缓解此问题。 幸运的是,我们的开发人员擅长命名事物things

Type safety

更大的问题是,尽管类型别名为我们提供了不同的名称,但它们并不是真正的不同类型,因此我们实际上并不是安全的类型。

让我们来看一个Java示例,其中的两个功能接口具有相同的方法签名。

JavaInterface1 f1 = item -> "Print " + item;
JavaInterface2 f2 = item -> "Print " + item;
f1 = f2;Error: java: incompatible types: JavaInterface2 cannot be converted to JavaInterface1

这就是我们期望的,我们不想在这里混合苹果和橙子。

如果我们使用Kotlin类型别名做同样的事情,会发生什么? (我想你知道我要去哪里了)

var f1: KotlinFunctionAlias1 = { item -> "Print $item" }
var f2: KotlinFunctionAlias2 = { item -> "Print $item" }
var f3: (Item) -> String = { item -> "Print $item" }
f1 = f2
f2 = f3
f1 = f3

这样做很好,编译器不会抱怨,因为就像我提到的那样,它们实际上并不是不同的类型。 它们都是:(Item) -> String

Solutions

因此,让我们快速回顾一下解决Kotlin接口缺少的SAM转换以及Kotlin接口的优点和缺点的不同方法。

Leave functional interfaces as Java interfaces

+良好的Java互操作性 +支持自定义方法名称 +类型安全

-需要给Kotlin lambda加上接口名称前缀 -需要额外的括号 -需要维护Java代码

Use a type alias for Kotlin function types

+良好的Java互操作性 +易于使用

-不安全输入 -没有自定义方法名称

Use inline classes

我们尚未讨论的另一种选择是使用实验性Kotlin内联类。 您可以使用内联类“包装” Kotlin函数。

inline class KotlinInlineInterface(val doSomething: (Item) -> String)fun delegateInlineWork(f: KotlinInlineInterface): String {return f.doSomething.invoke(item)
}delegateInlineWork(KotlinInlineInterface { "Print $it" })

Even though this works, I don't thinks it's an appropriate way of using inline classes. Also Java interoperability isn't currently supported: https://kotlinlang.org/docs/reference/inline-classes.html#mangling

Always use Kotlin function types

是的,您可以使用(ParamT) -> ReturnT类型无处不在。 通常这就足够了,但是随着您的应用程序的增长,它可能会变得更难阅读和维护,并且更容易出错。

Live with anonymous objects

当然,如果您不介意,则可以只使用匿名对象,希望有一天Kotlin将支持完整的SAM转换,并利用出色的IDE集成将您的匿名对象迁移到lambdas。

¯\(ツ)/¯

Jetbrains Feedback

There has been a short discussion on Reddit: https://www.reddit.com/r/Kotlin/comments/bipj0q/functional_interfaces_selfloathing_in_kotlin/

从那时起,我得到了罗马·伊里扎洛夫(Roman Elizarov)的回应

我尝试了提到的Kotlin编译器选项:

// Gradle Kotlin DSL
tasks.withType<KotlinCompile> {kotlinOptions.freeCompilerArgs += "-XXLanguage:+NewInference"
}
// Gradle Groovy DSL
compileKotlin {kotlinOptions {freeCompilerArgs += "-XXLanguage:+NewInference"}
}

If you're more into other build systems, refer to Kotlin documentation (Maven / Ant) to see how to pass Kotlin compiler arguments.

Problem solved?

首先让我们看看在Kotlin代码中使用Kotlin功能接口时会发生什么:

fun delegateWork(f: KotlinInterface): String {return f.doSomething(item)
}delegateWork { item -> "Print: $item" }Error: Type mismatch: inferred type is (Nothing) -> TypeVariable(_L) but KotlinInterface was expected

显式指定接口呢?

delegateWork(KotlinInterface { item -> "Print $item" }Error: Interface KotlinInterface does not have constructors

mm! 我们仍然需要一个匿名对象。

怎么样使用Kotlin中的Java功能接口码?

fun javaInterface(f: JavaInterface) {val res = f.doSomething(item)output(res)
}javaInterface { item -> "Print: $item" }

最后:正是我们所期望的。 一切都很好,啤酒当之无愧!

Patience young Jedi

如果您观察的话,会在构建过程中看到以下内容:

w: ATTENTION!
This build uses unsafe internal compiler arguments:
-XXLanguage:+NewInferenceThis mode is not recommended for production use,
as no stability/compatibility guarantees are given on
compiler or generated code. Use it at your own risk!

那是什么意思呢? 这意味着它在这里所说的内容:使用起来还不是很安全。 但是了解到JetBrains正朝着这个方向努力时,我建议我们暂时按照以下方式进行操作(从最有利到最不利)

  1. 将功能接口保留为Java代码为Kotlin函数类型使用类型别名(如果您可以将苹果和橙子混合使用)与匿名对象一起生活

谢谢阅读。 一如既往,我乐于接受批评和反馈。

from: https://dev.to//ranilch/functional-interfaces-self-loathing-in-kotlin-4bce

这篇关于功能接口:Kotlin中的自我厌恶的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/233900

相关文章

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

C语言小项目实战之通讯录功能

《C语言小项目实战之通讯录功能》:本文主要介绍如何设计和实现一个简单的通讯录管理系统,包括联系人信息的存储、增加、删除、查找、修改和排序等功能,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录功能介绍:添加联系人模块显示联系人模块删除联系人模块查找联系人模块修改联系人模块排序联系人模块源代码如下

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

Java CompletableFuture如何实现超时功能

《JavaCompletableFuture如何实现超时功能》:本文主要介绍实现超时功能的基本思路以及CompletableFuture(之后简称CF)是如何通过代码实现超时功能的,需要的... 目录基本思路CompletableFuture 的实现1. 基本实现流程2. 静态条件分析3. 内存泄露 bug

C#实现系统信息监控与获取功能

《C#实现系统信息监控与获取功能》在C#开发的众多应用场景中,获取系统信息以及监控用户操作有着广泛的用途,比如在系统性能优化工具中,需要实时读取CPU、GPU资源信息,本文将详细介绍如何使用C#来实现... 目录前言一、C# 监控键盘1. 原理与实现思路2. 代码实现二、读取 CPU、GPU 资源信息1.

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

详解Java如何向http/https接口发出请求

《详解Java如何向http/https接口发出请求》这篇文章主要为大家详细介绍了Java如何实现向http/https接口发出请求,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 用Java发送web请求所用到的包都在java.net下,在具体使用时可以用如下代码,你可以把它封装成一

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

基于WinForm+Halcon实现图像缩放与交互功能

《基于WinForm+Halcon实现图像缩放与交互功能》本文主要讲述在WinForm中结合Halcon实现图像缩放、平移及实时显示灰度值等交互功能,包括初始化窗口的不同方式,以及通过特定事件添加相应... 目录前言初始化窗口添加图像缩放功能添加图像平移功能添加实时显示灰度值功能示例代码总结最后前言本文将