为什么我被迫在Kotlin中使以下函数属性为null


package algorithms
import algorithms.util.IOUtils
object Calculator {
/* static abstract class Operation {
*   private Function logic; //functional interface
*   Operation(Function f) { this.logic = f );
* }
*/
sealed class Operation(val logic : (Int, Int)-> Int)
/* define singleton types 'ADD', 'SUB', 'MUL', 'DIV' - equivalent to inner class definitions in Java */
/* static class ADD extends Operation {
*   ADD(){
*      super((int x, int y)-> x+y);
*   }
* }
*/
object ADD: Operation({ x: Int , y: Int -> x+y } )
object SUB: Operation({ x: Int , y: Int -> x-y })
object MUL: Operation({ x: Int , y: Int -> x*y })
object DIV: Operation({ x: Int , y: Int -> x/y })
private fun getOperationFromChar(ch : Char): Operation? {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> null
}
}
fun eval(ch: Char, x: Int, y: Int): Int? {
val op : Operation? = getOperationFromChar(ch)
return op?.logic?.invoke(x,y)
}
}

fun main(){
println("Result : ${Calculator.eval(
IOUtils.readChar("Enter desired operation (+,-,*,/) "),
IOUtils.readInteger("Enter first number"),
IOUtils.readInteger("Enter second number"))}")
}

上面的代码运行良好,但是IntelliJ迫使我在return op?.logic?.invoke(x,y)可为空

尽管Operationsealed class Operation(val logic : (Int, Int)-> Int)的定义没有提到它可以为null。

我想如果Operation对象的定义是sealed class Operation(val logic : ((Int, Int)-> Int)?),那么这是有意义的,但事实并非如此。这是怎么回事?

这是因为getOperationFromChar()的返回值可以为null。

返回可为null值的不是您的操作函数。op本身已经可以为null。你用val operation: Operation?自己定义的。当您使用?.调用时,结果总是可以为null的,因为如果没有对象可以调用函数,则null将是结果。

getOperationFromChar()函数的输入是Char。Char可以是成千上万个可能值中的任何一个,而不仅仅是when语句中的四个。这就是编译器强制执行else分支的原因。如果你想避免返回一个可为null的值,你可以选择在给出无效输入时抛出一个错误:

private fun getOperationFromChar(ch : Char): Operation {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> error("Invalid input $ch")
}
}

然后您可以定义val op: Operation,它将能够接受此函数的结果为不可为null。

当密封类是when主题的类型时,密封类有助于避免需要else分支。然后编译器可以确保您为每个可能的输入都有一个分支。函数的情况正好相反,密封的类类型是输出,而不是输入。

顺便说一句,在这种情况下,使用枚举而不是密封类更明智,因为密封类的子级都没有唯一的属性或函数。

如果你把链式评估拆开,它会变得很清楚:

fun eval(ch: Char, x: Int, y: Int): Int? {
val op: Operation? = getOperationFromChar(ch)
val logic: ((Int, Int) -> Int)? = op?.logic
val retval: Int? = logic?.invoke(x, y)
return retval
}

逻辑不是类型((Int,Int(->Int(但是((Int,Int(->Int(,因为如果op为null,则op?的结果?。逻辑也将为null。

这是因为op可以为null;若将op的类型设置为Operation,则不需要检查logic的可空性。

实际上,调用invoke()方法是检查整个op?.logic的可空性;其可以由于CCD_ 22的可空性而抛出CCD_。

如果左边的值为null,则安全调用运算符(?.(返回null,否则继续计算右边的表达式。例如

val x:Int? = 4
x?.dec()?.inc()?.dec()

x?.let { 
it.dec().inc().dec()
}

相关内容

  • 没有找到相关文章

最新更新