Kotlin扩展函数:Any?.toString()

4

我一直在尝试使用Kotlin的扩展函数。

class ExtensionExample() {

   var name: String? = null

   fun Any?.toString() : String {

       if (this == null) {
           return "Value is null"
       }

       return "Value is not null"
   }
}

当我像下面这样打印name变量时:
println(ExtensionExample().name.toString())

它应该像这样打印:

Value is null

但是它打印的结果不像我预期的那样,只会打印null
请有人解释一下吗?
3个回答

8

扩展函数作用域。这就是为什么它在ExtensionExample类之外不可访问的原因。

通常,扩展函数被定义为顶级函数。也就是说,它们往往属于类之外

您的toString扩展是在ExtensionExample内定义的。这是可能的,但这意味着它只在该类的范围内可用,而且仅限于该类

您的main方法很可能是一个顶级函数(而不是该类的成员),因此无法访问此扩展函数。

只有其他成员才能访问此扩展函数:

class ExtensionExample {

    var name: String? = null

    fun Any?.toString(): String {
        return if (this == null) "Value is null" else "Value is not null"
    }

    fun foo() {
        println(name.toString())
    }

}

fun main(args: Array<String>) {
    ExtensionExample().foo()
}

打印"Value is null"

在线试用!

它能够编译的原因是调用的toString方法是来自kotlin-stdlibAny?.toString方法。也就是说,这个方法已经存在

fun Any?.toString(): String

返回对象的字符串表示形式。可以使用空接收器调用,在这种情况下,它将返回字符串"null"。

将您的扩展函数移出类。然后,您的扩展函数将隐藏stdlib的扩展函数,任何在相同包中调用而没有显式导入kotlin.toString的调用都将使用您的函数。

以下是您的代码的工作版本

class ExtensionExample {

    var name: String? = null

}

fun Any?.toString(): String {
    return if (this == null) "Value is null" else "Value is not null"
}

fun main(args: Array<String>) {
    println(ExtensionExample().name.toString())
}

按照你的要求,打印出"Value is null"

在线尝试!


请注意,您不能使用扩展函数隐藏成员函数。也就是说,
class Test {

    fun a() {
        println("a")
    }

}

fun Test.a() {
    println("b")
}

...
Test().a()

这将导致打印出 "a"。

你的 Any?.toString 之所以能够工作(在被移出类之后)仅仅是因为 Any? 没有成员,因为它不是一个类,而是一个带有类 Any 的可空类型。已经存在的 toString 方法也是一个扩展方法,因此你的方法不会被任何成员隐藏。


将我的扩展函数移出类,但仍然没有成功。 - Zumry Mohamed
@ZumryMohamed 你确定吗?我的第一个代码片段对我有效,并且它打印出“值为null”。这是一个ideone链接 - Salem
无论如何,你的回答很棒。尽管你的第一个回答失败了,但第二次编辑让我理解了很多。我喜欢你的解释。我以后还会向你请教其他问题的。谢谢。 - Zumry Mohamed

3

在类内部定义扩展函数:

如果你想要将toString()的定义保留在ExtensionExample类中,那么你只能从这个类中使用它:

class ExtensionExample() {
    var name: String? = null

    // uses the toString() extension function defined in ExtensionExample
    fun useToString() = name.toString()

    fun Any?.toString(): String {

        if (this == null) {
            return "inside: Value is null"
        }
        return "inside: Value is not null"
    }
}

This

// uses the version of toString() defined in the Kotlin Standard library
println(ExtensionExample().name.toString()) 

// uses the inside version of toString()
println(ExtensionExample().useToString())

将打印

null
内部:值为null

在类外定义扩展函数:

但如果你想在外部使用它,你必须将它移到外部:

class ExtensionExample() {
    var name: String? = null

    // uses the toString() extension function defined in ExtensionExample
    fun useToString() = name.toString()

    fun Any?.toString(): String {

        if (this == null) {
            return "inside: Value is null"
        }
        return "inside: Value is not null"
    }
}

fun Any?.toString(): String {

    if (this == null) {
        return "outside: Value is null"
    }
    return "outside: Value is not null"
}

This

// uses the outside version of toString() which overrides the one from the Kotlin Standard library
println(ExtensionExample().name.toString())  

// uses the inside version of toString()
println(ExtensionExample().useToString())

将会打印:

外部:值为null
内部:值为null


0
如果一个类有一个成员函数,并且定义了一个扩展函数,该扩展函数具有相同的接收器类型、相同的名称并适用于给定的参数,则成员始终获胜。
您的扩展方法不能遮盖AnyObject)类上内置的toString方法。
至于在可空类型上调用toString,此方法已经在Kotlin运行时中。
/**
 * Returns a string representation of the object. Can be called with a null receiver, in which case
 * it returns the string "null".
 */
public fun Any?.toString(): String

有关扩展名的解析,请参阅 文档


请纠正我,但扩展函数并没有遮蔽 Any.toString(也根本不在作用域内)。 - Salem
嗯,没错。它不仅不能遮蔽它,而且也不在作用域内。感谢纠正! - zsmb13

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接