覚えたら書く

IT関係のデベロッパとして日々覚えたことを書き残したいです。twitter: @yyoshikaw

Kotlin - 静的なユーティリティクラスなしで関数を定義する

Javaの場合、メソッドは必ずクラスに属する必要があります。
対象のクラスの操作を提供するという意味ではこの形は良いものであると思います。

ただし、業務アプリケーションなどを作成していくうちに、どうしても特定のクラスに属さないユーティリ的なメソッドというものも登場してきます。
その様な場合でも何らかのクラスに属する必要があります。
こういった際には、一般的に xxxUtils という名称のクラスが登場することになります。

例えば、特定の文字列の前後をprefixとpostfixの文字列で装飾する decorate というメソッドを定義するとした場合、以下の様なコードを書くことになります。

package net.yyuki.sandbox.string;

public final class StringUtils {

    public static String decorate(String src, String prefix, String postfix) {
        return new StringBuilder(prefix)
                    .append(src)
                    .append(postfix)
                    .toString();
    }

    private StringUtils() {
    }
}

呼び出し側コードは以下の様になります

import net.yyuki.sandbox.string.StringUtils;

public class Main {
    public static void main(String[] args) {
        String baseStr = "something";
        String result = StringUtils.decorate(baseStr, "<", ">");

        System.out.println(baseStr + " -> " + result);
    }
}

実行結果は以下の通りです

something -> <something>


Kotlinの場合はトップレベルに関数を配置できる

Kotlinでは上記の様な意味の薄いクラスを作成する必要なしに、ソースファイルのトップレベル(=クラスの外側)に関数を置くことができます。
この関数は、ファイルのトップに宣言されたパッケージのメンバとなります。それら関数を他パッケージから呼び出す場合は、今まで通りインポートは必要ですが、不要なレベルのネストは存在しなくなります。


Javaで定義した StringUtils#decvorateStrings.ktというファイルを作成して定義し直すと以下の様になります

package net.yyuki.sandbox.string

import java.lang.StringBuilder

fun decorate(src: String, prefix: String, postfix: String): String {
    return StringBuilder(prefix)
        .append(src)
        .append(postfix)
        .toString()
}

(Kotlinでの)呼び出し側のコードは以下の様になります

import net.yyuki.sandbox.string.decorate

fun main() {
    val baseStr = "kotlin is jvm lang"
    val result = decorate(src = baseStr, prefix = "{", postfix = "}")

    println("$baseStr -> $result")
}

実行結果は以下の通りです

kotlin is jvm lang -> {kotlin is jvm lang}


Kotlinで定義した関数をJavaから呼び出す

Kotlinで定義した関数をKotlinから呼び出す場合はクラスは登場しなくなっていますが、
Kotlinで定義した関数をJavaから呼び出す場合はクラス名を意識する必要があります。

さきほどStrings.kt というファイルに decorate というメソッドを定義しましたが、Javaからはこの関数は StringsKtクラスの decorate メソッドとして呼び出す事ができます。

実際のJavaからの呼び出しのコードは以下の様になります

import net.yyuki.sandbox.string.StringsKt;

public class Main {
    public static void main(String[] args) {
        String baseStr = "something";
        String result = StringsKt.decorate(baseStr, "(", ")");

        System.out.println(baseStr + " -> " + result);
    }
}


Kotlinのトップレベル関数を含む、生成されたクラスの名前を変更する場合は、ファイルに@JvmNameアノテーションを追加します。
ファイルの冒頭(=パッケージ名の前)にアノテーションを配置する必要があります。

例えば、Java向けのクラス名を StringsOperation にしたい場合は以下の様になります

@file:JvmName("StringsOperation")

package net.yyuki.sandbox.string

import java.lang.StringBuilder

fun decorate(src: String, prefix: String, postfix: String): String {
    return StringBuilder(prefix)
        .append(src)
        .append(postfix)
        .toString()
}

Javaからの呼び出しのコードは以下の様になります

import net.yyuki.sandbox.string.StringsOperation;

public class Main {
    public static void main(String[] args) {
        String baseStr = "something";
        String result = StringsOperation.decorate(baseStr, "(", ")");

        System.out.println(baseStr + " -> " + result);
    }
}