假设我有一个用于服务器响应的密封类:
sealed class Response{
class Success: Response()
class ErrorA: Response()
class ErrorB: Response()
}
一个虚假的回答:
fun getResponse(): Response{
val r = Random()
return when (r.nextInt(3)) {
0 -> { Response.Success() }
1 -> { Response.ErrorA() }
2 -> { Response.ErrorB() }
else -> { throw IllegalStateException() }
}
}
我想处理回应。我现在可以使用这样的东西:
fun handle(response: Response) = when (response) {
is Response.Success -> { handle(response) }
is Response.ErrorA -> { handle(response) }
is Response.ErrorB -> { handle(response) }
}
然后编译器将确保处理所有情况。一个令人敬畏的功能!
为什么我不能做这样的事情:
class ResponseHandler(){
fun handle(success: Response.Success) {}
fun handle(error: Response.ErrorB) {}
fun handle(error: Response.ErrorA) {}
}
然后打电话
ResponseHandler().handle(response)
这实现了同样的事情,但不编译,我的问题是:就像编译器在运行时确保所有情况都在when
语句中处理一样,为什么同样的逻辑不能应用于方法重载?
任何信息或推荐进一步阅读将是非常有帮助的。谢谢
3条答案
按热度按时间exdqitrt1#
原则上,这是可以做到的(基本上是通过自动生成
handle(response: Response) = when ...
方法)。但我觉得这不太可能。Kotlin中的重载基本上与Java/Scala/其他JVM语言中的重载相同,并且引入如此小的好处的主要差异看起来不是一个好主意(当然这不适用于Kotlin特定的when
)。如果需要,可以在
ResponseHandler
中定义相同的fun handle(response: Response)
(并将其他handle
方法设置为open
,这样实际上就很有用了)。von4xj4u2#
这个问题可以分解为这个简化的例子:
您有两个专门的方法,分别接受
Int
和Double
。您的值的类型为Number
(Int
和Double
的超类型)。虽然i
显然是一个整数,但变量的类型为Number
,它不能作为calc(i: Int)
或calc(d: Double)
的参数。在您的例子中,您得到一个
Response
,并希望调用一个重载方法,这些方法都不直接接受Response
。ugmeyewa3#
对于任何对此感兴趣的人,我花了很长时间试图找到一种具有我在问题中描述的动态行为的语言,但同时维护一个真实的的类型系统。问了这个问题两年后,我遇到了朱莉娅,并了解到描述我试图实现的目标的短语是multiple dispatch。
我的问题源于
sealed class
提供的功能。这本质上是“求和类型”,允许编译器保证每个子类型都包含在when
表达式中。它确实是一个编译时特性。多重分派是实现以下功能的一个功能:当方法被调用时,语言在运行时确定哪个函数最适当地匹配类型,而不是在编译时确定要调用的函数。换句话说,它运行与参数最匹配的函数。
Kotlin没有多重分派,这可以通过以下方式来证明:
尝试运行此命令会导致以下编译时错误:
尽管
numbers[0]
和numbers[1]
的值分别具有Int
和Float
类型,但编译器在编译时无法知道这一点,因此不会调用add(a: Int, b: Float)
。与支持多分派的语言相比,这是一个很大的区别。Julia就是这样一种语言,实际上它使多重分派成为一个核心特性。上面的例子可以在Julia中这样实现:
然后调用
add
看起来像这样:Julia确定在运行时调用哪个方法。
与所有编程语言的设计特性一样,每种选择都有一组权衡,通常适用于不同的上下文。Kotlin和Java能够有广泛的工具集成,因为类型可以在编译时确定。Julia能够成为一种具有更高表现力的动态语言,但牺牲了一些静态分析。