如何在 Kotlin 中将 "own type" 指定为 return 类型
How to specify "own type" as return type in Kotlin
有没有办法将函数的return类型指定为被调用对象的类型?
例如
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
实际上,这大致相当于 Objective-C 中的 instancetype
或 Swift 中的 Self
。
没有语言功能支持这一点,但您始终可以使用递归泛型(这是许多库使用的模式):
// 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() {}
}
您可以使用 extension method 来实现 "returns same type" 效果。这是一个快速示例,它显示了具有多个类型参数的基本类型和一个扩展方法,该扩展方法采用对所述类型的实例进行操作的函数:
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
。
你可以 return 使用 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()
}
递归类型绑定
您在问题中展示的模式在 JVM 世界中被称为递归类型绑定。递归类型包括一个函数,该函数使用该类型本身作为其参数或其 return 值的类型。在您的示例中,您通过说 return this
.
为 return 值使用相同的类型
例子
让我们通过一个简单而真实的例子来理解这一点。我们将用 interface
替换您示例中的 trait
,因为 trait
现在在 Kotlin 中已弃用。在此示例中,接口 VitaminSource
return 对不同维生素来源的不同实现。
在下面的interface
中,可以看到其类型参数以自身为上界。这就是为什么它被称为递归类型绑定:
VitaminSource.kt
interface VitaminSource<T: VitaminSource<T>> {
fun getSource(): T {
@Suppress("UNCHECKED_CAST")
return this as T
}
}
我们禁止显示 UNCHECKED_CAST
警告,因为编译器不可能知道我们是否传递了相同的 class 名称作为类型参数。
然后我们用具体的实现扩展interface
:
Carrot.kt
class Carrot : VitaminSource<Carrot> {
fun getVitaminA() = println("Vitamin A")
}
Banana.kt
class Banana : VitaminSource<Banana> {
fun getVitaminB() = println("Vitamin B")
}
在扩展 classes 时,您必须确保将相同的 class 传递给接口,否则您将在运行时得到 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()
}
就是这样!希望对您有所帮助。
您也可以通过扩展功能来实现。
class Foo
fun <T: Foo>T.someFun(): T {
return this
}
Foo().someFun().someFun()
根据确切的用例,scope functions can be a good alternative. For the builder pattern 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
时有效 。如果其中一个方法实际上创建了一个新实例,那么该实例将被丢弃。
这是基于 类似的问题。
有没有办法将函数的return类型指定为被调用对象的类型?
例如
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
实际上,这大致相当于 Objective-C 中的 instancetype
或 Swift 中的 Self
。
没有语言功能支持这一点,但您始终可以使用递归泛型(这是许多库使用的模式):
// 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() {}
}
您可以使用 extension method 来实现 "returns same type" 效果。这是一个快速示例,它显示了具有多个类型参数的基本类型和一个扩展方法,该扩展方法采用对所述类型的实例进行操作的函数:
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
。
你可以 return 使用 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()
}
递归类型绑定
您在问题中展示的模式在 JVM 世界中被称为递归类型绑定。递归类型包括一个函数,该函数使用该类型本身作为其参数或其 return 值的类型。在您的示例中,您通过说 return this
.
例子
让我们通过一个简单而真实的例子来理解这一点。我们将用 interface
替换您示例中的 trait
,因为 trait
现在在 Kotlin 中已弃用。在此示例中,接口 VitaminSource
return 对不同维生素来源的不同实现。
在下面的interface
中,可以看到其类型参数以自身为上界。这就是为什么它被称为递归类型绑定:
VitaminSource.kt
interface VitaminSource<T: VitaminSource<T>> {
fun getSource(): T {
@Suppress("UNCHECKED_CAST")
return this as T
}
}
我们禁止显示 UNCHECKED_CAST
警告,因为编译器不可能知道我们是否传递了相同的 class 名称作为类型参数。
然后我们用具体的实现扩展interface
:
Carrot.kt
class Carrot : VitaminSource<Carrot> {
fun getVitaminA() = println("Vitamin A")
}
Banana.kt
class Banana : VitaminSource<Banana> {
fun getVitaminB() = println("Vitamin B")
}
在扩展 classes 时,您必须确保将相同的 class 传递给接口,否则您将在运行时得到 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()
}
就是这样!希望对您有所帮助。
您也可以通过扩展功能来实现。
class Foo
fun <T: Foo>T.someFun(): T {
return this
}
Foo().someFun().someFun()
根据确切的用例,scope functions can be a good alternative. For the builder pattern 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
时有效 。如果其中一个方法实际上创建了一个新实例,那么该实例将被丢弃。
这是基于