如何指定;“自己的类型”;作为Kotlin中的返回类型

如何指定;“自己的类型”;作为Kotlin中的返回类型,kotlin,Kotlin,有没有办法将函数的返回类型指定为被调用对象的类型 e、 g trait Foo{ 乐趣条():/*在这里放什么?*/{ 还这个 } } 类别Foo类别a:Foo{ 乐趣a(){} } 类别Foo类别b:Foo{ 乐趣b(){} } //这是理想的效果: val a=FooClassA().bar()//应为FooClassA类型 a、 a()//这样就行了 val b=FooClassB().bar()//应为FooClassB类型 b、 b()//这样就行了 实际上,这大致相当于Object

有没有办法将函数的返回类型指定为被调用对象的类型

e、 g

trait Foo{
乐趣条():/*在这里放什么?*/{
还这个
}
}
类别Foo类别a:Foo{
乐趣a(){}
}
类别Foo类别b:Foo{
乐趣b(){}
}
//这是理想的效果:
val a=FooClassA().bar()//应为FooClassA类型
a、 a()//这样就行了
val b=FooClassB().bar()//应为FooClassB类型
b、 b()//这样就行了

实际上,这大致相当于Objective-C中的
instancetype
或Swift中的
Self

没有支持这一点的语言功能,但您始终可以使用递归泛型(这是许多库使用的模式):

//定义递归泛型参数Me
特色食品{
趣味酒吧:我{
//这里我们必须强制转换,因为编译器不知道Me与这个类相同
把这个还给我
}
}
//在子类中,将自身作为参数传递给超类:
类别Foo类别a:Foo{
乐趣a(){}
}
类别Foo类别b:Foo{
乐趣b(){}
}
您可以使用来实现“返回相同类型”的效果。下面是一个快速示例,它显示了一个具有多个类型参数的基类型和一个扩展方法,该扩展方法接受一个在所述类型的实例上运行的函数:

public abstract class BuilderBase<A, B> {}

public fun <B : BuilderBase<*, *>> B.doIt(): B {
  // Do something
  return this
}

public class MyBuilder : BuilderBase<Int,String>() {}

public fun demo() {
  val b : MyBuilder = MyBuilder().doIt()
}
public抽象类BuilderBase{}
公共娱乐B.doIt():B{
//做点什么
还这个
}
公共类MyBuilder:BuilderBase(){}
公共娱乐示范(){
val b:MyBuilder=MyBuilder().doIt()
}

由于扩展方法是静态解析的(至少从M12开始),如果需要特定于类型的行为,您可能需要让扩展将实际实现委托给它的
this

您可以使用返回自己的类型

接口示例接口
//实现ExampleInterface的所有东西都将具有此方法。
乐趣T.doSomething():T{
还这个
}
ClassA类:示例接口{
fun classASpecificMethod(){}
}
类ClassB:示例接口{
fun classBSpecificMethod(){}
}
有趣的例子(){
//doSomething()返回ClassA!
ClassA().doSomething().classASpecificMethod()
//doSomething()返回ClassB!
ClassB().doSomething().classBSpecificMethod()
}
递归类型绑定 问题中显示的模式在JVM世界中称为递归类型绑定。递归类型包括一个函数,该函数使用该类型本身作为其参数或返回值的类型。在您的示例中,您对返回值使用相同的类型,方法是说
returnthis


例子 让我们用一个简单而真实的例子来理解这一点。我们将用
interface
替换您示例中的
trait
,因为
trait
现在在Kotlin中已被弃用。在本例中,接口
VitaminSource
返回不同维生素源的不同实现

在下面的
界面中
,您可以看到其类型参数本身是一个上限。这就是为什么它被称为递归类型绑定:

VitaminSource.kt

interface VitaminSource<T: VitaminSource<T>> {
    fun getSource(): T {
        @Suppress("UNCHECKED_CAST")
        return this as T
    }
}
class Carrot : VitaminSource<Carrot> {
    fun getVitaminA() = println("Vitamin A")
}
class Banana : VitaminSource<Banana> {
    fun getVitaminB() = println("Vitamin B")
}
fun main() {
    val carrot = Carrot().getSource()
    carrot.getVitaminA()

    val banana = Banana().getSource()
    banana.getVitaminB()
}
香蕉.kt

interface VitaminSource<T: VitaminSource<T>> {
    fun getSource(): T {
        @Suppress("UNCHECKED_CAST")
        return this as T
    }
}
class Carrot : VitaminSource<Carrot> {
    fun getVitaminA() = println("Vitamin A")
}
class Banana : VitaminSource<Banana> {
    fun getVitaminB() = println("Vitamin B")
}
fun main() {
    val carrot = Carrot().getSource()
    carrot.getVitaminA()

    val banana = Banana().getSource()
    banana.getVitaminB()
}
测试.kt

interface VitaminSource<T: VitaminSource<T>> {
    fun getSource(): T {
        @Suppress("UNCHECKED_CAST")
        return this as T
    }
}
class Carrot : VitaminSource<Carrot> {
    fun getVitaminA() = println("Vitamin A")
}
class Banana : VitaminSource<Banana> {
    fun getVitaminB() = println("Vitamin B")
}
fun main() {
    val carrot = Carrot().getSource()
    carrot.getVitaminA()

    val banana = Banana().getSource()
    banana.getVitaminB()
}


就这样!希望对您有所帮助。

您也可以通过扩展功能来实现

class Foo
fun <T: Foo>T.someFun(): T {
    return this
}
Foo().someFun().someFun()
class-Foo
例如:T{
还这个
}
Foo().someFun().someFun()

Trait在Kotlin M12中被弃用,我们将使用关键字接口:编译器有没有办法推断正确的行为?或者,如果绝对有必要强制转换,则禁止从Kotlin IDE插件发出警告:
未选中强制转换:Foo to Me
?太棒了!聪明的回答。我想知道2021年有没有更好的?无论如何,很好的回答如果Kotlin支持使用类似于
Self
a la-Rust的东西引用实现类型,那就太好了,但是考虑到这是我们正在讨论的JVM语言,我认为这是一个白日梦@OzShabat,除了这种普通汤,真的没有别的办法了。坦率地说,这样的代码与在C中直接通过vtables实现面向对象这样的古怪行为没有什么不同。@breandan不,不是kotlin不够聪明,就是JVM的考虑因素阻止了它的尝试。如果是第一种情况,情况会好转。如果是后者,那么请为java从一开始就不断地攻击自己而感到惋惜。我正试图找出相同的答案,但对于基本类和子类: