注意
正直この記事の内容に実用性はあまりないと思います。
はじめに
この記事は Scala Advent Calendar 2019 10日目の記事です。9日目は @cactaceae さん、11日目は @pictiny さんの Scalaで日付の範囲を指定してリストを作るです。
手元でちょっとしたプログラムを実行したいとき、REPLもいいですが、やはりIntelliJの強力な補完が使いたいということでPlayground的なプロジェクトをsbtで作ってそれをIntelliJ IDEAで開いてる人もいると思います。
自分もそういった感じで普段使っていたのですが、自分はScalaの他に普段Kotlinも書いていて、KotlinのほうはGradleでPlayground的なプロジェクトを用意していて、どうせならそちらでScalaも使えたほうが楽だよねということでやってみたことと、思いつきでKotlinのコードをScalaから呼び出してみたらできてしまったのでそれを記事にすることにしました。
サンプルを私のGitHubに公開しています。
https://github.com/yt8492/KotlinAndScalaPlayground
執筆時の環境
IntelliJ IDEA 2019.3 Ultimate Edition
GradleプロジェクトにScalaを導入する
Gradleで適当に作ったKotlinのプロジェクトにScalaを導入していきます。
build.gradle
pluginsブロックにidを追加します。
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
id 'scala' // 追加
}
dependenciesブロックにScalaの依存を追加します。
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.scala-lang:scala-library:2.12.6' // 追加
}
Scalaディレクトリの追加
main直下のkotlinディレクトリなどがある階層と同じところにscalaディレクトリを作ります。
適当にコードを実行してみる
先程作ったディレクトリにファイルを追加します。
object Main {
def main(args: Array[String]): Unit = {
println("Hello Scala")
}
}
この時点でIntelliJのRunが実行できますが、Gradleのタスクでのやり方も解説しておきます。
build.gradleのpluginsブロックにapplicationを追加し、MainClassNameを指定します。
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
id 'scala'
id 'application' // 追加
}
mainClassName = "Main" // 追加
タスクを実行してみます。
./gradlew run
期待通りの結果が出力されると思います。
Hello Scala
ScalaからKotlinのコードを呼び出す
適当にKotlinのコードを用意します。
class Foo {
fun bar() {
println("bar")
}
}
先程のScalaのコードを書き換えて呼び出してみます。
object Main {
def main(args: Array[String]): Unit = {
val foo = new Foo()
foo.bar()
}
}
このままでもIDEのRunから実行できますが、gradleのrunタスクで実行しようとすると以下のようなエラーメッセージが出て失敗するので、build.gradleを修正していきます。
projectPath/src/main/scala/Main.scala:3: not found: type Foo
val foo = new Foo()
以下のブロックを追加するだけです。
compileScala {
classpath += files(compileKotlin.destinationDir)
}
これで
./gradlew run
でも実行できるようになったと思います。
ScalaからKotlinがどのように見えているか
基本的にはJavaからKotlinを呼び出すときと同じです。
トップレベル関数は ファイル名Kt.関数名
で呼び出します。
fun hoge() {
println("hoge")
}
HogeKt.hoge()
ScalaとKotlinの両方にある機能であるデフォルト引数は、Kotlin側で @JvmOverloads
をつけないと使えませんでした。やはりJavaから呼び出すときと同じようです。
class Foo {
@JvmOverloads
fun baz(arg: String = "baz") {
println(arg)
}
}
val foo = new Foo()
foo.baz("bar") // bar
foo.baz() // baz
実行はできるのですが、IDE上でエラーが表示されていますね。
nullの扱いに関しては、やはりJavaからKotlinを呼び出すときと同じで、non-nullな引数にnullを渡すと実行時に落ちます。
Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method Foo.baz, parameter arg
at Foo.baz(Foo.kt)
at Main$.main(Main.scala:4)
at Main.main(Main.scala)
まとめ
- ScalaからKotlinを呼び出すことは、ScalaからJavaを呼び出すこととJavaからKotlinを呼び出すことと同じ
- IDEでエラーがでるあたり本来想定されている使われ方ではなさそう
- 実用性はあまりないと思われる