Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I call extension methods from outside the class they are defined in?

Here is a minimal example that demonstrates the problem:

abstract class Base {
    abstract fun String.extension(x: Char)
}

class Derived : Base() {
    override fun String.extension(x: Char) {
        // Calling lots of methods on String, hence extension method
        println("${first()} $length ${last()} ${firstOrNull { it == x }} ...")
    }
}

Calling the extension method from Java is trivial:

Base o = new Derived();
o.extension("hello world", 'l');

But I can't figure out how to do it in pure Kotlin. Neither String nor Base seems to have the extension method.

like image 545
fredoverflow Avatar asked Mar 12 '17 11:03

fredoverflow


People also ask

How do you call an extension method?

To define and call the extension methodDefine a static class to contain the extension method. The class must be visible to client code. For more information about accessibility rules, see Access Modifiers. Implement the extension method as a static method with at least the same visibility as the containing class.

What are extension methods and where are they defined?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

Can you add extension methods to an existing static class?

The main advantage of the extension method is to add new methods in the existing class without using inheritance. You can add new methods in the existing class without modifying the source code of the existing class. It can also work with sealed class.

What keyword is used to define an extension method?

Extension Method uses the "this" keyword as the first parameter. The first parameter always specifies the type that the method operates on. The extension method is written inside a static class and the method is also defined as static.


2 Answers

First, note that an extension function defined as a member requires two receivers, one is an instance of the enclosing class (dispatch receiver, usually this of the enclosing class) and the other is the instance of the type that the function extends (extension receiver). This is documented here.

So, to call such a function from outside the class you have to provide two receivers. Kotlin doesn't have any syntax to do that explicitly like (x, "abc").stringExtension(), but you can provide the dispatch receiver implicitly using an extension lambda:

class C(val name: String) {
    fun String.extended() = this + " extended by " + name
}

fun main(args: Array<String>) {
    val c = C("c")
    with(c) { println("abc".extended()) }
}

(runnable demo of this code)

In the with(...) { ... } block, c becomes the implicit receiver, thus allowing you to use it as a dispatch receiver in C member extensions. This would work with any other function that uses functional types with receivers: apply, run, use etc.

In your case, it would be with(o) { "hello world".extension('l') }

As @KirillRakhman noted, an extension receiver of an extension function for C can also be implicitly used as a dispatch receiver for the extensions defined inside C:

fun C.someExtension() = "something".extended()
like image 122
hotkey Avatar answered Oct 05 '22 08:10

hotkey


To decale an extension method outside the class using it, you should NOT implement it inside a class, and you should do like this:

package com.sample.test

import java.io.File

fun File.folderLength() : Long {
    return 0L
}

So in your class that call this method:

package com.sample.util

import com.sample.test.*
import java.io.File

class foo{
    fun getFolderSize(url: String) : Long{
        var file = new File("...")
        var length = file.folderLength()
        return length
    }
}

Hope this should help you.

like image 33
JuniperPhoton Avatar answered Oct 05 '22 08:10

JuniperPhoton