Reputation: 9558
I have been trying to use extension functions in Kotlin.
class ExtensionExample() {
var name: String? = null
fun Any?.toString() : String {
if (this == null) {
return "Value is null"
}
return "Value is not null"
}
}
When I print the name variable like below
println(ExtensionExample().name.toString())
It should print like
Value is null
But it doesn't print like I expected. It only prints null
.
Can someone explain please?
Upvotes: 4
Views: 5909
Reputation: 14907
Extension functions have scopes. This is why it is not accessible outside of the ExtensionExample
class.
Usually, extension functions are defined as top-level functions. That is, they tend to belong outside of a class.
Your toString
extension is defined within ExtensionExample
. This is possible, but it means it is only available in the scope of this class, and this class only.
Your main
method is likely a top-level function (and not a member of that class), so it doesn't have access to this extension function.
Only other members can access this extension function:
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()
}
prints "Value is null"
The reason why it compiles is that the toString
method that is being called is the Any?.toString
method from the kotlin-stdlib. That is, this method already exists.
fun Any?.toString(): String
Returns a string representation of the object. Can be called with a null receiver, in which case it returns the string "null".
Move your extension function out of the class. Your extension function will then hide the stdlib's extension, and any invocation in the same package not explicitly importing kotlin.toString
will use your function.
Here is a working version of your code:
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())
}
prints "Value is null"
, as you wanted.
Note that you cannot hide a member function using an extension function. That is,
class Test {
fun a() {
println("a")
}
}
fun Test.a() {
println("b")
}
...
Test().a()
will result in "a" being printed.
The only reason why your Any?.toString
works (after being moved out of the class) is that Any?
has no members as it is not a class, but a nullable type with the class Any
. The already existing toString
method is also an extension, so yours is not hidden by any members.
Upvotes: 8
Reputation: 29844
Defining an extension function inside a class:
If you want to leave the definition of toString()
within ExtensionExample
, you can only use it from there:
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())
will print
null
inside: Value is null
Defining an extension function outside a class:
But if you want to use it outside, you have to move it outside:
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())
will print:
outside: Value is null
inside: Value is null
Upvotes: 3
Reputation: 89548
If a class has a member function, and an extension function is defined which has the same receiver type, the same name and is applicable to given arguments, the member always wins.
Your extension method can't shadow the built-in toString
method on the Any
(Object
) class.
As for calling toString
on a nullable type, this method is already in the Kotlin runtime:
/**
* 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
See also the docs about the resolution of extensions.
Upvotes: 0