内部的智能投射和比较当'是'类型检查后的表达式

时间:2017-11-05 00:26:56

标签: java kotlin

根据When Expression的文档,它可以替换“if-else if”,所以我尝试实现一个函数来返回Any类型的两个变量的最大值:

fun maxOf(a: Any, b: Any) = when {
    a is Int && b is Int        -> if (a < b) b else a
    a is Double && b is Double  -> if (a < b) b else a
    a is Int && b is Double     -> if (a < b) b else a
    a is Double && b is Int     -> if (a < b) b else a
    a is String && b is String  -> if (a < b) b else a
    else                        -> null
}

上述实现有效,但我认为它可以更简洁:

fun maxOf(a: Any, b: Any) = when {
    (a is Int || a is Double) && (b is Int || b is Double) -> if (a < b) b else a
    a is String && b is String                             -> if (a < b) b else a
    else                                                   -> null
}

但我失败了,因为第二次实施不起作用;错误发生在第一次出现if (a < b)

  

未解决的参考资料。

     

由于接收器类型不匹配,以下候选者均不适用

     

public fun String.compareTo(other:String,ignoreCase:Boolean = ...):Int kotlin.text中定义

这是因为智能广播在评估表达式a后无法将b(a is Int || a is Double) && (b is Int || b is Double)投射到实际类型吗?或者我错过了什么?

更新

即使ab的类型更改为Number,也会发生同样的错误:

fun maxOf(a: Number, b: Number) = when {
    (a is Int || a is Double) && (b is Int || b is Double) -> if (a < b) b else a
    else                                                   -> null
}

1 个答案:

答案 0 :(得分:3)

为什么会这样?

这不是when表达的问题。你应该责怪类型系统。

abIntDouble的实例,因此Kotlin会将其推断为他们的LCA。例如:

open class A { fun a() = println("meow meow meow") }
class B : A()
class C : A()
if (a is B || a is C) a.a() // smart cast works

但是,IntDouble同时是NumberComparable的子类,Kotlin不知道您是否需要NumberComparable,因此Kotlin将其视为Any的实例。

if (a is Int || a is Double)
  if (a > 1) print("meow meow") // smart cast doesn't work

这就是出现“bug”的原因。

如何解决这个问题?

使用原始代码,或使用显式强制转换(我知道它很糟糕,但 确实是一个不可避免的问题)。

我想出了一个漂亮的解决方案!看看它:

fun <T : Comparable<T>> maxOf(a: T, b: T): T? = when {
    (a is Int || a is Double) && (b is Int || b is Double) -> if (a < b) b else a
    a is String && b is String -> if (a < b) b else a
    else -> null
}

通过这种方式,您可以对其应用Comparable(而不仅仅是IntDoubleString)!

fun <T : Comparable<T>> maxOf(a: T, b: T): T = if (a < b) b else a

如果输入无效,原始版本将返回null,如果输入无效,此版本将引发编译错误,帮助您在编译时发现错误。

不在话下!

你评论过我并提出了进一步的问题。我想让我的回复更具可读性,所以我会把它添加到我的回答中。

fun someFunction(a: Number) {
    if (a is Int || a is Double) println(a < 1) // still error! Why?!
}

是的,确实,aNumber的实例。 但请注意,<compareToNumber中声明 。它位于IntDouble:)。

编辑2

你说:

  

使用泛型强制ab属于同一类型。

所以试试这个:

fun <A : Comparable<B>, B : Any> maxOf(a: A, b: B): Any = if (a < b) b else a

你说:

  如果ab的类型为Any

显式广告不是解决方案

  • 解决方案零:创建operator fun Any.compareTo
  • 解决方案一:放弃铸造。