Java ではパッケージのトップレベルに関数を定義することはできませんでした(あるクラスの static メソッドとして定義する必要がありました)が、Kotlin ではクラスの外に関数(プロパティ、定数)を定義することができます。
下記の例では、util.kt
のトップレベルで printError
関数を定義し、それを main.kt
の中から利用しています。
package com.example
fun printError(message: String) {
System.err.println("Error: $message")
}
import com.example.printError
fun main() {
printError("ファイルが見つかりません")
}
main.kt
からの参照方法を見ると分かるように、Kotlin では .kt
ファイルの名前は参照時に意識する必要はなく、あくまでpackage により宣言されたパッケージ名だけが参照情報として必要になります。
トップレベルに定義された関数をまとめてインポートしたいときは、下記のようなワイルドカードを使用できます。
import com.example.*
パッケージのトップレベルに定義した関数(やプロパティ)は、デフォルトでは public になり、他の .kt
ファイルから参照できるようになっています。
可視性を自分のファイル内に限定したい場合は、private
を付けて関数を定義します。
package com.example.util
// これは他のファイルからも参照できる
fun greet(name: String) {
println(getMessage(name))
}
// これはこのファイルからしか参照できない
private fun getMessage(name: String) : String {
return "Hello, $name!"
}
package com.example
fun printError(message: String) {
System.err.println("Error: $message")
}
上記のように util.kt
ファイルの中で定義した com.example
パッケージの printError
関数は、Java (JVM) の世界からは com.example.UtilKt.printError
のように見えます。
Java の世界ではすべてのメソッドは何らかのクラスに所属していなければいけないので、util.kt
というファイル名から、自動的に UtilKt
という名前のクラスが生成されるようになっています。
import com.example.UtilKt;
public class Main {
public static void main(String... args) {
UtilKt.printError("こんにちは");
}
}
デフォルトでは .kt
ファイルの名前をベースに Java クラス名が生成されますが、@JvmName
アノテーションを使用すると、生成される Java クラス名を変更することができます。
@file:JvmName("LogUtil")
package com.example
fun printError(message: String) {
System.err.println("Error: $message")
}
上記の util.kt
ファイルのトップレベルに定義した関数は、UtilKt
ではなく、LogUtil
という名前の Java クラス内に定義されます。
よって、Java のコードからは下記のように参照することになります。
import com.example.LogUtil;
public class JavaMain {
public static void main(String... args) {
LogUtil.printError("こんにちは");
}
}
関数と同様に、プロパティもパッケージのトップレベルに定義することができます。
下記の例では、util.kt
ファイルのトップレベルに logPrefix
というプロパティを定義しています。
package com.example
var logPrefix = "Error: "
fun printError(message: String) {
System.err.println("${logPrefix}${message}")
}
このプロパティは、デフォルトで public なので、別の .kt
ファイルから参照することができます。
下記の例では、main.kt
ファイルの中から、util.kt
のトップレベルプロパティの値を変更しています(var
による変数定義なので値を変更できます)。
import com.example.logPrefix
import com.example.printError
fun main() {
logPrefix = "Hoge: "
printError("ファイルが見つかりません")
}
Hoge: ファイルが見つかりません
パッケージのトップレベルにプロパティを定義する際に、var
ではなく const val
を使用することで、トップレベルの定数として扱うことができます。
package com.example.math
const val PI = 3.14
import com.example.math.PI
fun main() {
println("円周率は $PI だよ")
}
定数を定義するときに、val
でなく、const val
と定義することで、Java の public static final
相当のバイトコードが生成されます。
const
を省略して val
と記述するだけでも同様に定数として扱うことはできるのですが、その場合、Kotlin は内部で getter メソッドを生成することになります。
Kotlin のコードからアクセスするときはどちらの場合も違いはないのですが、Java のコードからアクセスするときに明らかな違いが発生します。
const val PI = 3.14 // Java から見ると PI という定数
val PI = 3.14 // Java から見ると getPI() というメソッド