为什么我被迫在 Kotlin 中使以下函数 属性 可为空?
Why am I forced to make the below functional property nullable in Kotlin?
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 强制我在中制作 logic
return op?.logic?.invoke(x,y)
可为空
虽然Operation的定义sealed class Operation(val logic : (Int, Int)-> Int)
没有任何地方提到它可以为null。
我想如果 Operation 对象的定义是 sealed class Operation(val logic : ((Int, Int)-> Int)?)
那么它就有意义了,但事实并非如此。
这是怎么回事?
因为op
可以为空;如果将 op
的类型设置为 Operation
,则不需要检查 logic
.
的可空性
实际上,它检查整个op?.logic
的可空性以调用invoke()
方法;由于 op
可空性,可以抛出 NullPointerException
。
安全调用运算符(?.) returns null 如果左边的值为空,否则继续计算右边的表达式。
例如
val x:Int? = 4
x?.dec()?.inc()?.dec()
x?.let {
it.dec().inc().dec()
}
这是因为 getOperationFromChar()
的 return 值可以为空。
return不是您的操作函数可以为 null 的值。 op
本身已经可以为空。您自己用 val operation: Operation?
定义了它。当您使用 ?.
调用时,结果始终可以为 null,因为如果没有 object 来调用该函数,null
将是结果。
您的 getOperationFromChar()
函数的输入是一个字符。 Char 可以是数千个可能值中的任何一个,而不仅仅是 when
语句中的四个值。这就是编译器强制执行 else
分支的原因。如果你想避免 returning nullable,你可以选择在给出无效输入时抛出错误:
private fun getOperationFromChar(ch : Char): Operation {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> error("Invalid input $ch")
}
}
然后你可以定义 val op: Operation
并且它可以接受这个函数的结果作为 non-nullable.
当密封 class 是 when
的主题类型时,Sealed classes 有助于避免需要 else
分支。然后编译器可以确定您对每个可能的输入都有一个分支。你的函数是相反的情况,你的 sealed class 类型是输出,而不是输入。
顺便说一下,在这种情况下,使用枚举而不是密封 class 更明智,因为密封 [=] 的 children 中的 none 36=] 具有独特的属性或功能。
如果你把链式计算分开,就会变得很清楚:
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
}
logic 未键入 ((Int, Int) -> Int) 但 ((Int, Int ) -> Int)?,因为如果 op 为 null,则 op?.logic 的结果也将为 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 强制我在中制作 logic
return op?.logic?.invoke(x,y)
可为空
虽然Operation的定义sealed class Operation(val logic : (Int, Int)-> Int)
没有任何地方提到它可以为null。
我想如果 Operation 对象的定义是 sealed class Operation(val logic : ((Int, Int)-> Int)?)
那么它就有意义了,但事实并非如此。
这是怎么回事?
因为op
可以为空;如果将 op
的类型设置为 Operation
,则不需要检查 logic
.
实际上,它检查整个op?.logic
的可空性以调用invoke()
方法;由于 op
可空性,可以抛出 NullPointerException
。
安全调用运算符(?.) returns null 如果左边的值为空,否则继续计算右边的表达式。 例如
val x:Int? = 4
x?.dec()?.inc()?.dec()
x?.let {
it.dec().inc().dec()
}
这是因为 getOperationFromChar()
的 return 值可以为空。
return不是您的操作函数可以为 null 的值。 op
本身已经可以为空。您自己用 val operation: Operation?
定义了它。当您使用 ?.
调用时,结果始终可以为 null,因为如果没有 object 来调用该函数,null
将是结果。
您的 getOperationFromChar()
函数的输入是一个字符。 Char 可以是数千个可能值中的任何一个,而不仅仅是 when
语句中的四个值。这就是编译器强制执行 else
分支的原因。如果你想避免 returning nullable,你可以选择在给出无效输入时抛出错误:
private fun getOperationFromChar(ch : Char): Operation {
return when(ch){
'+' -> ADD
'-' -> SUB
'*' -> MUL
'/' -> DIV
else -> error("Invalid input $ch")
}
}
然后你可以定义 val op: Operation
并且它可以接受这个函数的结果作为 non-nullable.
when
的主题类型时,Sealed classes 有助于避免需要 else
分支。然后编译器可以确定您对每个可能的输入都有一个分支。你的函数是相反的情况,你的 sealed class 类型是输出,而不是输入。
顺便说一下,在这种情况下,使用枚举而不是密封 class 更明智,因为密封 [=] 的 children 中的 none 36=] 具有独特的属性或功能。
如果你把链式计算分开,就会变得很清楚:
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
}
logic 未键入 ((Int, Int) -> Int) 但 ((Int, Int ) -> Int)?,因为如果 op 为 null,则 op?.logic 的结果也将为 null。