【问题标题】:For a final class why does when complain?最后一堂课为什么会抱怨?
【发布时间】:2019-12-04 18:46:19
【问题描述】:

所以在下面的代码中,我得到编译错误“何时必须详尽添加必要的其他”:

class Test {

}

fun eval(e: Test): Int =
        when(e) {
            is Test -> throw IllegalArgumentException()
        }

为了澄清这只是旨在理解 Kotlin 的代码(这里是新手)。
所以 Test 在 Kotlin 的默认行为下不能被任何子类扩展。
那么when 期望的其他情况是什么?
在这种情况下使用sealed 也不起作用

【问题讨论】:

  • 疯狂猜测:1) 检查穷举的代码并没有检查这样的条件,因为有更好的替代方案 (if) 并且不值得付出努力。或者 2) 仅仅因为 Test 现在不开放,并不意味着它不会在未来开放。意思是,如果 eval() 被编译和交付,然后有人打开 Test 并继承它,eval() 将失败。
  • @Todd:关于 (2) 为什么eval 失败了?我会说这是一件好事,因为行为的变化(使其打开)会在编译时被捕获。
  • @Jim 我没有得到密封类的问题,即列出所有可能的值被认为是详尽的。 Kotlin 1.3.41 就是这样,仅供参考
  • @user2340612:你复制粘贴我的代码并使用关键字sealed吗?
  • @user2340612:另外我如何检查我的 IDE 使用的 Kotlin 版本?

标签: kotlin kotlin-when


【解决方案1】:

类测试

因为当需要返回默认值时,如果元素(e)不匹配任何东西,它应该返回默认值

class Test

fun eval(e: Test): Int =
when (e) {
    is Test -> throw IllegalArgumentException()
    else -> 7 //default value
}

其他选项是在函数中将返回类型更改为可为空

fun eval(e: Test): Int? =
when (e) {
    is Test -> throw IllegalArgumentException()
    else -> null
        }

何时

if when 用作表达式,else 分支是强制性的,除非编译器能证明所有 可能的情况用分支条件覆盖

.

更多详情请参考when-expression

如果元素是单一的时候就没用了。您的功能可以简化如下

fun eval(e: Test): Int = throw IllegalArgumentException() 

【讨论】:

  • 但这是我的问题。由于Test 不可能被子分类,所以else 分支可能属于什么?
  • 是的,你是对的。在这种情况下,不需要使用 'when' 使用 'if' 来代替。
  • 但我的问题是为什么when 不能处理这个?有没有我没有想到的可以替换的类型?
  • 你想抛出异常?
  • 请查看@Todd 的 cmets。看来我的问题不是每个人都清楚
【解决方案2】:

问题在于,当您想在 return/assign 语句中使用 when 时,它必须是详尽无遗的。

例如:

// let k be of Any? type:
when (k) {
    is Number -> { println("Ok, k is a Number") }
}

没关系。你不期望任何结果。如果k 是一个数字,则会打印一些内容。否则,什么都不会发生。

那么,第二个例子:

// k is the same
boolean isNumber = when (k) {
    is Number -> true;
}

但是,如果 boolean 不是数字怎么办?代码将无法编译,因为您的程序将处于不确定状态。

那么,你能做什么?您的when 必须详尽无遗。所以,只需添加else。例如,其中一种模式是抛出IllegalStateException() if case like your。

fun eval(e: Test): Int = when(e) {
    is Test -> 1
    else -> throw IllegalArgumentException("This should never happen!")
}

编辑:

关于您的评论。 e 实际上可能与 Test 不同。它可以是......什么都没有。

我完全了解可能发生的可能性以及此类使用的正确性。但是,下面的示例完全可以,并且编译没有任何问题。也许通过反射它会有一些有用的用途。

class Test

fun eval(e: Test): Int =
    when(e) {
        is Test -> throw IllegalArgumentException()
        else -> throw IllegalArgumentException()
    }

fun getNothing(): Nothing {
    throw IllegalStateException()
}

fun main() {
    eval(getNothing())
}

【讨论】:

  • 理解穷举。不明白的是,如果Test 不开放扩展,为什么他的eval() 并不详尽。在他的示例中,e 只能是 Test
  • @Todd:没错!
  • 我添加了一个小例子(理论上)如何传递不同于 Test 的东西(我知道这段代码总是会在运行时失败的情况下执行)。
【解决方案3】:

那么 when 期望的其他情况是什么? - “else”,因为 Test 不是密封类。

错误清楚地表明你需要添加else条件,我试图在else条件下返回0,因为函数返回整数并且错误消失了。

class Test {

}

fun eval(e: Test): Int =
    when(e) {
        is Test -> throw IllegalArgumentException()
        else -> 0 
    }

或者您需要分配给 var 或 val 并返回 Int 值,如下所示,

fun eval(e: Test): Int {
    val i: Int = when (e) {
        is Test -> throw IllegalArgumentException()
        else -> 0
    }
    return i
}

【讨论】:

    【解决方案4】:

    添加sealed 而不进行其他更改是没有意义的:它使类抽象并且不可能子类化,因此您将永远不会有实例。我相信这是一个编译器错误,它与https://youtrack.jetbrains.com/issue/KT-28249 非常相似,但使用class 而不是object

    这也是一个低影响的,因为如果is Test是唯一的分支,你可以用这个分支替换整个when,如果不是,你可以用@987654328替换is Test @。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-09
      • 2012-09-12
      • 2015-06-09
      • 2020-08-12
      • 2016-02-27
      • 1970-01-01
      相关资源
      最近更新 更多