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()
}