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

lztngnrs  于 2023-10-23  发布在  Kotlin
关注(0)|答案(7)|浏览(197)

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

trait Foo {
    fun bar(): <??> /* what to put here? */ {
        return this
    }
}

class FooClassA : Foo {
    fun a() {}
}

class FooClassB : Foo {
    fun b() {}
}

// this is the desired effect:
val a = FooClassA().bar() // should be of type FooClassA
a.a()                     // so this would work

val b = FooClassB().bar() // should be of type FooClassB
b.b()                     // so this would work

实际上,这将大致等同于instancetype或Swift中的Self

bvhaajcl

bvhaajcl1#

没有语言特性支持这一点,但你可以使用递归泛型(这是许多库使用的模式):

// Define a recursive generic parameter Me
trait Foo<Me: Foo<Me>> {
    fun bar(): Me {
        // Here we have to cast, because the compiler does not know that Me is the same as this class
        return this as Me
    }
}

// In subclasses, pass itself to the superclass as an argument:
class FooClassA : Foo<FooClassA> {
    fun a() {}
}

class FooClassB : Foo<FooClassB> {
    fun b() {}
}
8nuwlpux

8nuwlpux2#

你可以用extension functions返回一些东西自己的类型。

interface ExampleInterface
// Everything that implements ExampleInterface will have this method.
fun <T : ExampleInterface> T.doSomething(): T {
    return this
}

class ClassA : ExampleInterface {
    fun classASpecificMethod() {}
}

class ClassB : ExampleInterface {
    fun classBSpecificMethod() {}
}

fun example() {
    // doSomething() returns ClassA!
    ClassA().doSomething().classASpecificMethod()
    // doSomething() returns ClassB!
    ClassB().doSomething().classBSpecificMethod()
}
eqzww0vc

eqzww0vc3#

您可以使用extension method来实现“返回相同类型”的效果。这里有一个快速的例子,展示了一个带有多个类型参数的基类型和一个扩展方法,该方法接受一个对所述类型的示例进行操作的函数:

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()
}

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

aiqt4smr

aiqt4smr4#

递归类型绑定

您在问题中显示的模式在JVM世界中被称为 * 递归类型绑定 *。递归类型是包含一个函数的类型,该函数使用该类型本身作为其参数或返回值的类型。在您的示例中,您使用相同的类型作为返回值,即return this

示例

让我们用一个简单而真实的例子来理解这一点。我们将用interface替换示例中的trait,因为trait现在在Kotlin中已被弃用。在本例中,接口VitaminSource返回不同维生素源的不同实现。
在下面的interface中,您可以看到其类型参数将自身作为上限。这就是为什么它被称为递归类型绑定:

VitaminSource.kt

interface VitaminSource<T: VitaminSource<T>> {
    fun getSource(): T {
        @Suppress("UNCHECKED_CAST")
        return this as T
    }
}

我们取消了UNCHECKED_CAST警告,因为编译器不可能知道我们是否传递了相同的类名作为类型参数。
然后我们用具体的实现扩展interface

胡萝卜.kt

class Carrot : VitaminSource<Carrot> {
    fun getVitaminA() = println("Vitamin A")
}
  • Banana.kt**
class Banana : VitaminSource<Banana> {
    fun getVitaminB() = println("Vitamin B")
}

在扩展类时,必须确保将相同的类传递给接口,否则将在运行时获得ClassCastException

class Banana : VitaminSource<Banana>    // OK
class Banana : VitaminSource<Carrot>    // No compiler error but exception at runtime

Test.kt

fun main() {
    val carrot = Carrot().getSource()
    carrot.getVitaminA()

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

就这样!希望对你有帮助。

qaxu7uf2

qaxu7uf25#

根据具体的使用情况,scope functions可能是一个很好的替代方案。对于构建器模式,apply似乎是最有用的,因为上下文对象是this,作用域函数的结果也是this
考虑这个例子,一个List的构建器有一个专门的构建器子类:

open class ListBuilder<E> {
    // Return type does not matter, could also use Unit and not return anything
    // But might be good to avoid that to not force users to use scope functions
    fun add(element: E): ListBuilder<E> {
        ...
        
        return this
    }

    fun buildList(): List<E> {
        ...
    }
}

class EnhancedListBuilder<E>: ListBuilder<E>() {
    fun addTwice(element: E): EnhancedListBuilder<E> {
        addNTimes(element, 2)
        return this
    }

    fun addNTimes(element: E, times: Int): EnhancedListBuilder<E> {
        repeat(times) {
            add(element)
        }
        return this
    }
}

// Usage of builder:
val list = EnhancedListBuilder<String>().apply { 
    add("a") // Note: This would return only ListBuilder
    addTwice("b")
    addNTimes("c", 3)
}.buildList()

然而,这只有在所有方法都有this作为结果时才有效。如果其中一个方法实际上创建了一个新示例,那么该示例将被丢弃。
这是基于this answer的一个类似的问题。

xu3bshqb

xu3bshqb6#

class foo(){
fun boo():foo{
return this
  }
}
vojdkbi0

vojdkbi07#

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

class Foo
fun <T: Foo>T.someFun(): T {
    return this
}
Foo().someFun().someFun()

相关问题