返回类型为“单位?”,不是覆盖的子类型

时间:2019-08-09 17:26:44

标签: android kotlin

今天,在编程时,我在Kotlin中发现了一些奇怪的行为。我可以轻而易举地解决它,但是我想知道是出于某种原因还是Kotlin中的错误。

我具有以下委托接口,该接口将对话框的显示委托给Activity。

interface ViewModelDelegate {
fun showWarningDialog(textResource: Int)
}

我想在“活动”中按以下方式实现它。因为我知道我只能在上下文中执行此操作,并且Activity.getContext()可能返回null,所以我将代码包装在context?.let

override fun showWarningDialog(textResource: Int) = context?.let {
//show dialog
}

但是这给了我一个编译错误:

  

'showWarningDialog'的返回类型不是重写成员'public abstract fun showWarningDialog(textResource:Int):com.some.class.path中定义的单元”的返回类型的子类型。

这真的使我感到困惑,因为我不想退货。因此,由于let返回内部返回的任何函数,所以我想知道是否可以通过编写一个不返回任何内容的let版本来修复它。

fun <T, R> T.myLet(block: (T) -> R) {
    let(block)
}

但是,这并没有消除编译器错误。然后,我发现该错误上的鼠标悬停文本提供了更多信息(如果编译器这样做的话会很好)。它说:

  

返回类型为'Unit?',它不是重写的子类型

现在,这可以告诉我更多有关该问题的信息。因为可能不会发生函数context?let的调用,所以它可能返回null。现在有多种方法可以解决此问题。我可以在函数调用的末尾添加?: Unit,也可以定义showWarningDialog返回Unit?,这将使我在大多数情况下可以很好地调用它。但是,这些解决方案都不是可取的。我可能只会做一个普通方法并在其中调用let而不是委托它。花了我另一个缩进级别和一条额外的垂直线:

override fun showWarningDialog(textResource: Int) {
    context?.let {
        //show dialog
    }
}

我的问题是,这种行为是故意的吗?为什么或何时有用,以致不能将返回Unit的函数委派给可选函数调用。这种行为让我很困惑。

2 个答案:

答案 0 :(得分:1)

单表达式功能

fun foo() = <expression>

通过语言设计等同于

fun foo(): <ReturnType> {
 return <expression>
}

并且由于Unit?不是Unit的子类型,因此您不能从返回Unit的函数中将其返回。从这个意义上说,Unit只是类型系统中的另一种类型,并不是什么神奇的东西。因此,它就像可以与任何其他类型一起使用一样。

  

为什么或何时有用,以致不能将返回Unit的函数委派给可选函数调用。

因此,基本上的问题是,为什么语言设计人员没有创建一个特殊处理来接受来自将Unit?声明为返回类型的函数中的Unit。我可以考虑以下几个原因:

  1. 需要在编译器中创建此特殊处理。特殊情况会导致错误,打破苗条的语言设计并使文档复杂化。
  2. 由于必须是特殊情况,所以对于程序员而言,这不是很清楚且不可预测。目前,它对所有类型都以相同的方式工作,没有特殊处理。它使语言具有可预测性,您无需查看每种类型的文档即可查看是否经过特殊处理。
  3. 它还增加了一些附加的安全性,以便使您注意到表达式实际上可以跳过计算。

因此,总结一下,我想说,使此案例行之有效,并没有增加太多价值,但可能会带来一些问题。这可能就是为什么他们没有将其添加到语言中的原因。

答案 1 :(得分:0)

让我们讨论当您有返回类型(例如String

)时的这种情况
interface someInterface{
    fun somFun():String
}
class someClass : someInterface {
    var someString:String? = null
    override fun somFun()=someString?.let { 
        //not working
        it
    }

    override fun somFun()=someString?.let {
        //working
        it
    }?:""
}

所以我们看到,当父母的返回类型为String时,您不能返回Strin?,这是绝对的nullSafety, 没有返回类型时有什么不同?让我们稍微改变一下代码

interface someInterface{
    fun somFun():String
    fun unitFun()
}
class someClass : someInterface {
    var someString:String? = null

    override fun unitFun() {
       //if it is possible to return null in here 
    }

    override fun somFun()=someString?.let {
        val someresult = unitFun().toString() //you will get crash
        it
    }?:""
}

现在我们有了另一个没有返回类型(unitFun Unit)的函数 因此,如果您可以在子类中返回Unit?,则在使用方法的结果时将导致崩溃,因为它的定义为Unit,并且您不需要进行任何null检查。 / p>

通常,这意味着Unit也是类型,您需要将其保留为空。