Using Kotlin extensions in Groovy

Extensions in Kotlin and GroovyKotlin and Groovy have mechanisms for extending existing classes without using inheritance or decorators. In both languages, the mechanisms are called extension methods. Their source code looks different, but generated by…

Using Kotlin extensions in Groovy

Extensions in Kotlin and Groovy

Kotlin and Groovy have mechanisms for extending existing classes without using inheritance or decorators. In both languages, the mechanisms are called extension methods. Their source code looks different, but generated bytecode is quite similar. Thanks to that, Groovy is able to use Kotlin extensions just like its own.

Why would I want to use such extensions in Groovy? The main reason is that I want to test my extensions using the best testing framework available for the JVM – Spock Framework.

Code is available here.

Extensions in Kotlin

There are many types of extensions in Kotlin. I decided to focus only on extension functions and properties.

As an example, I extend the java.lang.String class. First, I create an extension function skipFirst, which skips first N characters:

fun String.skipFirst(n: Int) = if (length > n) this.substring(n) else ""

 

Next, I create an extension property answer, which is the Answer to the Ultimate Question of Life, the Universe, and Everything:

val String.answer
    get() = 42

Both extensions are declared in package com.github.alien11689.extensions, in file called StringExtensions. However, the generated class in target directory is named StringExtensionsKt and this is the name that must be used when accessing from other languages. Specific class name can be forced by annotation @file:JvmName.

Using Kotlin extensions in Groovy

There are two ways for using extensions in Groovy that are supported by good IDEs. First, you can declare scope where the extensions are available by use method:

def "should use extension method"() {
    expect:
        use(StringExtensionsKt) {
            input.skipFirst(n) == expected
        }
    where:
        input  | n | expected
        "abcd" | 3 | "d"
        "abcd" | 6 | ""
        ""     | 3 | ""
}

def "should use extension property"() {
    expect:
        use(StringExtensionsKt) {
            "abcd".answer == 42
        }
}

It is acceptable, but is not very convenient. The second and much better way is to use an extension module definition. The extension module is defined in file org.codehaus.groovy.runtime.ExtensionModule in directory src/main/resources/META-INF/services/. The same directory is monitored by ServiceLoader, but the file format is completely different:

moduleName=string-extension-module
moduleVersion=1.0.0
extensionClasses=com.github.alien11689.extensions.StringExtensionsKt

The tests look much better now:

def "should use extension method"() {
    expect:
        input.skipFirst(n) == expected
    where:
        input  | n | expected
        "abcd" | 3 | "d"
        "abcd" | 6 | ""
        ""     | 3 | ""
}

def "should use extension property"() {
    expect:
        "abcd".answer == 42
}
You May Also Like

Use asInstanceOf[T] carefully!

BackgroundScala has nice static type checking engine but from time to time there are situations when we must downcast some general object. If this casting is not possible we expect that virtual machine will throw ClassCastExeption as fast as possible. ...

Grails with Spock unit test + IntelliJ IDEA = No thread-bound request found

During my work with Grails project using Spock test in IntelliJ IDEA I've encountered this error:

java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.currentRequestAttributes(CommonWebApi.java:205)
at org.codehaus.groovy.grails.plugins.web.api.CommonWebApi.getParams(CommonWebApi.java:65)
... // and few more lines of stacktrace ;)

It occurred when I tried to debug one of test from IDEA level. What is interesting, this error does not happen when I'm running all test using grails test-app for instance.

So what was the issue? With little of reading and tip from Tomek Kalkosiński (http://refaktor.blogspot.com/) it turned out that our test was missing @TestFor annotation and adding it solved all problems.

This annotation, according to Grails docs (link), indicates Spock what class is being tested and implicitly creates field with given type in test class. It is somehow strange as problematic test had explicitly and "manually" created field with proper controller type. Maybe there is a problem with mocking servlet requests?

Mentoring in Software Craftsmanship

Maria Diaconu and  Alexandru Bolboaca are both strong supporters of Software Craftsmanship and Agile movements in Romania, with years of experience as developers, leaders, architects, managers and instructors. On their lecture at Agile Central Eur...Maria Diaconu and  Alexandru Bolboaca are both strong supporters of Software Craftsmanship and Agile movements in Romania, with years of experience as developers, leaders, architects, managers and instructors. On their lecture at Agile Central Eur...