以下をベースに文法をザックリまとめ。Scala とだいたい同じ文法になってる。
http://confluence.jetbrains.net/display/Kotlin/Welcome
ちなみに、はてなのスーパーpre記法がkotlinにまだ対応してないので、コードのキーワード色つけがされません・・
パッケージ
Scala とほとんど同じ
package foo.bar // ...
ネストして書くこともできる
package a { package b { val x = 1 package a { val y = package.root.a.b.x } } }
- パッケージを指定しない場合にはルートパッケージに属する
- 物理ディレクトリ構成と合致している必要はない
- パッケージの参照は相対的に行われる
- 相対的に解決されるためルートから指定するには package.root とする
- 上記例では val y = a.b.x とするとパッケージ階層を解決できない
インポート
import foo.Bar import hoge.* import bar.Bar as bBar
- Java と同じ書き方
- アスタリスクでまとめてインポート
- asにて別名インポート
package.root でルートから指定できる
import package.root.a.b.a.*
変数
val と var にて指定
- valは read-only
- varは Mutable
文字列と文字列テンプレート
文字列は以下のようなリテラル表記ができる
val s = "Hello, world!\n"
- 型は推論されるため省略可能
- 文字のエスケープはJavaと同じ
トリプルクオートにてraw文字列として扱える
val text = """ for (c in "foo") print(c) """
- 改行やクオートをそのまま文字列として定義できる
文字列テンプレートで変数を文字列に埋め込める
val i = 10 val s = "i = $i"
- sは "i = 10" となる
変数のプロパティなどにアクセスする場合は以下
val s = "abc" val str = "$s.length is ${s.length}"
- sは "$s.length is 3" となる
基本的な型
Kotlin での型はすべてオブジェクトでプリミティブ型は無い。
数値型
型 | ビット数 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
リテラル表記
- 10進: 123, 123.5, 123.5e10
- 16進Hexadecimals: 0x0F
- バイナリ: 0b00001011
文字
文字は Char で、数値型との互換性はない。つまり以下はエラー
fun check(c : Char) { if (c == 1) { } }
数値型として取り扱うには toInt() などを使う
fun decimalDigitValue(c : Char) : Int { return c.toInt() - '0'.toInt() }
リテラル表記として '1', '\n', '\uFF00' のようにシングルクオートを使う
論理型
Boolean で表す。|| と && 演算が可能
val b : Boolean = true
列挙
enum にて指定
enum class Direction { NORTH SOUTH WEST EAST }
- Javaと同じように引数を取ったりもできる
関数
fun キーワードではじめる
fun double(x : Int) : Int { return x * 2 }
呼び出しは
val two = demo(1)
一つの式で完結できる場合は、= で繋げて return も不要
fun double(x : Int) : Int = x * 2
上記は戻り値が型推論されるため以下のように書ける
fun double(x : Int) = x * 2
明示的な戻り値が無い関数は Unit を指定
fun printHello(name : String?) : Unit { if (name != null) print("Hello, $name!") else print("Hi there!") }
Unit は省略して書ける(=は書かない)
fun printHello(name : String?) { //... }
ローカル関数
関数のなかに関数を書くとローカル関数になる
fun dfs(graph : Graph){ fun dfs(current : Vertex, visited : Set<Vertex>) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v, visited) } dfs(graph.vertices[0], HashSet()) }
ローカル関数は、それを包む関数が持つローカル変数を見ることができる(つまりクロージャ)
fun dfs(graph : Graph){ val visited = HashSet<Vertex>() fun dfs(current : Vertex) { if (!visited.add(current)) return for (v in current.neighbors) dfs(v) } dfs(graph.vertices[0]) }
上記例では visited 変数をローカル関数が参照できる
ローカル関数から、直接それを包む関数の戻り値を返却することができる
fun reachable(from : Vertex, to : Vertex) : Boolean { val visited = HashSet<Vertex>() fun dfs(current : Vertex) { if (current == to) return@reachable true // ・・・ } }
- return@reachable にて外部の関数からリターンする(trueを返す)
ジェネリック関数
型パラメータでジェネリック関数を定義
fun singletonArray<T>(item : T) : Array<T> { val result = Array<T>(1) result[0] = item return result }
可変長引数
可変長引数は vararg にて指定する
fun asList<T>(vararg ts : T) : List<T> { //・・・ }
- vararg はデフォルトで Array 型
vararg の型は以下のように変更できる
fun asList<T>(vararg<ArrayList<T>> ts : T) : List<T> = ts
- ts は ArrayList 型となる
デフォルト引数
関数の引数にはデフォルト値を指定できる
fun read(b : Array<Byte>, off : Int = 0, len : Int = -1) { val actualLength = if (len == -1) b.length else len // ... }
呼び出しは以下のようにデフォルト値は省略可能
read(b, off, len) read(b, off) read(b)
名前付き引数
以下のような関数がある場合、
fun reformat( str : String, normalizeCase : Boolean = true, uppercaseFirstLetter : Boolean = true, divideByCamelHumps : Boolean = false, wordSeparator : Character = ' ' ) { // ... }
引数の名前を指定して呼び出せる
reformat(str, normalizeCase = true, uppercaseFirstLetter = true, divideByCamelHumps = false, wordSeparator = '_' )
デフォルト引数があるので、以下のようにも書ける
reformat(str, wordSeparator = '_')
関数呼び出し
引数が1つの関数は()を省略できる
1 shl 2
は、以下の略となる
1.shl(2)
- 1 foo 2 bar 3 の場合には (1 foo 2) bar 3 のように結合する
拡張関数
既存の型を拡張して関数を追加したように扱える
Int 型を拡張して abs() 関数を追加
fun Int.abs() : Boolean = if (this >= 0) this else -this -1.abs()
- 拡張関数は静的に解決される
以下のようなリテラルで書くこともできる
val sum = {Int.(other : Int) : Int -> this + other}
- Int を拡張し、sum という名前の関数を定義
- sum はIntの引数を取り、Intの戻り値を返す関数
以下のように呼び出せる
1.sum(2)
または
1 sum 2
ジェネリックな場合
fun <T> LinkedList<T>.swap(x : Int, y : Int) { val tmp = this[x] // 'this' corresponds to the list this[x] = this[y] this[y] = tmp }
以下のように呼び出せる
val l = linkedList(1, 2, 3) l.swap(0, 2)
高階関数
高階関数とは、関数を引数にとったり、関数を戻り値として返却する関数
fun lock<T>(lock : Lock, body : () -> T) : T { lock.lock() try { return body() } finally { lock.unlock(); } }
- 関数は () -> T という型として指定している
- body は引数なし、T型の戻り値を返却する関数
呼び出しは以下
val result = lock(lock, { sharedResource.operation() })
- {}は関数リテラル
関数の最後の引数パラメータが関数の場合には以下のように制御構造風に書ける
lock (lock) { sharedResource.operation() }
拡張関数の引数に関数を指定した例
fun <T, R> List<T>.map(transform : (T) -> R) : List<R> { val result = ArrayList<R>() for (item in this) result.add(transform(item)) return result }
- T型を引数にとり、戻り値がR型の関数 を渡せる
- Listの拡張関数で、map 関数を追加している
呼び出しは以下
val doubled = ints.map {it -> it * 2}
- it を引数に取り、it * 2 の結果を返却する関数リテラルを渡している
- 引数が1つの場合には以下のように省略できる
ints map {it * 2}
関数リテラル
関数はリテラルとして以下の様に定義できる
val sum = {(x : Int, y : Int) : Int -> x + y}
関数 sum の型を指定すれば以下のようにも書ける
val sum : (Int, Int) -> Int = {(x, y) -> x + y}
そして()も省略できる
val sum : (Int, Int) -> Int = {x, y -> x + y}
パラメータが1つの場合は省略して以下のように書ける
ints.filter {it > 0}
- Intを引数に取り、Booleanを返す関数 '(it : Int) -> Boolean' がリテラル表記されている
クラス
class で指定し、メンバとして以下を含むことができる
- 関数
- プロパティ
- 他のクラス
- Object
class Example(param : Int) { val property = param }
- クラス名の後に引数を指定することでプライムラリコンストラクタとなる(Scalaと同じ)
以下のようにインスタンス化する。newは使わない
val e = Example(10)
コンストラクタでの何らかの初期化処理が必要な場合はカーリーブレスで指定
class ExampleWithAnonymousInitializer(param : Int) { val property = HashSet<Int>(); { property.add(param) } }
コンストラクタには var や val を指定して以下のようにも定義できる
class Bean(val prop1 : Int, val prop2 : String)
- クラスのボディは無くても良い
クラスのなかに関数を定義すればメンバ関数となる
class Sample() { fun foo() { print("foo") } } Sample().foo()
ジェネリックなクラスは以下
class Box<T>(t : T) { var value = t } val box : Box<Int> = Box<Int>(1)
型推論で以下のようにインスタンス化できる
val box = Box(1)
内部クラスとthis限定子
クラスの中にクラスを書くと内部クラスとなる
class Outer() { private val bar : Int = 1 class Nested() { fun foo() = 2 } } val demo = Outer.Inner().foo() // == 2
クラスや関数をネストした場合、this@ としてthis参照を限定できる
class A { // @A と暗黙的にラベル化 class B { // @B と暗黙的にラベル化 fun Int.foo() { // @foo と暗黙的にラベル化 val a = this@A // Aクラスを指す val b = this@B // Bクラスを指す val c = this // foo()のレシーバである Int クラスを指す val c1 = this@foo // foo()のレシーバである Int クラスを指す val funLit = {String.() -> val d = this // funLit 関数のレシーバ val d1 = this@ // funLit 関数のレシーバ } val funLit2 = { (s:String) -> val d1 = this // foo()のレシーバ } } } }
オブジェクト
Scalaと同じで Singleton のインスタンスは object で定義
object DataProviderManager { fun registerDataProvider(provider : DataProvider) { // ... } val allDataProviders : Collection<DataProvider> get() = // ... }
kotlin には static はない。static な関数はオブジェクトを使う
class C() { class object { fun create() = C() } } fun main() { val c = C.create() }
けどまだ実装されていない模様
トレイト
ある。だいたいScalaと同じ。実装をモテるインターフェース。
trait MyTrait{ fun bar() { //・・・ } }
プロパティ
Java の Bean に相当するものは以下の定義で済む
public class Address() { public var name : String = ... public var street String = ... public var city : String = ... public var state : String? = ... }
- Javaのようにgetter/setterは使わない
- Javaバイトコードにコンパイルされた段階でgetter/setterが生成される
- var で宣言して読み書き用のプロパティ
- val で宣言すると読み込み専用プロパティとなる
getter/setter をカスタマイズする時は、プロパティの宣言に続いて以下のように書く
var stringRepresentation : String get() = this.toString() set(value) { setDataFromString(value) }
プロパティ自身への代入は以下のようにする
var counter = 0 set(value) { if (value >= 0) $counter = value }
読み取り専用として入れ物を確保しなくても良い
val isEmpty : Boolean get() = this.size > 0
制御構造 if
if式は値を返せる
val max = if (a > b) { print("Choose a") a } else { print("Choose b") b }
- 最後に評価された値が戻り値となる
制御構造 when
Javaのswitchの置き換え。パターンマッチが行える。
when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { print("x is neither 1 nor 2") } }
カンマで繋いで複数指定できる
when (x) { 0, 1 -> print("x == 0 or x == 1") else -> print("otherwise") }
when は is を使うことで型のパターンマッチができる
when (x) { is Int -> print(x + 1) is String -> print(x.length + 1) is Array<Int> -> print(x.sum()) !is Number -> print("Not even a number") }
- 型をマッチング
- !isで否定も扱える
制御構造 for
for-each として以下のように書ける
for (item in collection) print(item)
制御構造 while
while(x > 0) { x-- } do { val y = retrieveData() } while(y != null)
例外
kotlinには検査例外はない。例外をスローするには
throw MyException("Hi there!")
try は Java と同じ
try { // some code } catch (e : SomeException) { // handler } finally { // optional finally block }
tryは値を返すこともできる
val a : Int? = try { parseInt(input) } catch (e : NumberFormatException) { null }
nullセーフ
kotlin に nullPointerExceptionはない。以下はコンパイラにてエラーとなる。
var a : String = "abc" a = null // compilation error
null となりうる変数は型として?を付ける必要がある
var b : String? = "abc" b = null // ok
Elvis operator
以下のif式は
val l : Int = if (b != null) b.length() else -1
以下のように書ける
val l = b?.length() ?: -1
- nullだった場合のデフォルトの戻り値を指定
!! operator
nullだったら例外を投げるには
val l = b!!.length()
型キャスト
if にて is で判定すると自動的にキャストされる(スマートキャスト)
fun demo(x : Any) { if (x is String) { print(x.length) } }
以下の記述もアリ
if (obj !is String) { print("Not a String") } else { print(obj.length) }
安全ではないキャスト
asにてキャストでき、キャストに失敗すると例外となる
val x : String = y as String val a : String? = b as String?
as?とするとキャストに失敗した場合にはnullが返る
val x : String? = y as? String
同一性
Java の == に相当する同一インスタンスを識別する組込の操作はない。以下を使う。
a.identityEquals(b)
// または
a identityEquals b
Ranges
Rangesのリテラルは .. で指定
if (a in 1..100) { print("in range") }
for にてイテレートできる
for (x in 1..100) { print(x) }
Array
Array は以下のような定義となっている
class Array<T>(val size : Int, init : (Int) -> T) { fun get(index : Int) : T fun set(index : Int, value : T) : Unit fun iterator() : Iterator<T> val indices : IntRange }
[]や in はgetやset、iterator() としてコンパイルされるため以下のように扱える
val array = array(1, 2, 3, 4) array[x] = array[x] * 2 for (x in array) print(x)
index でのアクセスは以下
for (i in array.indices) array[i] += 2
ifでは範囲にあるかがチエックできる
if (i in array.indices) { print(array[i]) }
- (i >= 0 && i < array.size) と同じ
継承
全てのクラスはAnyを暗黙的に継承する
class Example
クラスを継承するにはコロンでつなげる
open class Base(p : Int) class Derived(p : Int) : Base(p)
Kotlinのクラスはデフォルトでfinalなので、継承するには親に open が宣言されている必要がある
open は Java で言うfainalの反対の意味になる
オーバーライドを行うには override アノテーションが必要
open class Base { open fun v() {} fun nv() {} } class Derived() : Base() { override fun v() {} }
open は関数にも付けることで override 可能となる
open class AnotherDerived() : Base() { final override fun v() {} }
final を付けるとこれ以上のオーバーライドは行えなくなる
override だけだと open のまま
trait にて複数のオーバーライド対象ができる場合には、サブクラス側で必ず override する必要がある。
open class A { open fun f() { print("A") } fun a() { print("a") } } trait B { open fun f() { print("B") } open fun b() { print("b") } } class C() : A(), B { // The compiler requires f() to be overridden: override fun f() { super<A>.f() // call to A.f() super<B>.f() // call to B.f() } }
上記の f は実装が2つあるので、クラス C で override しなければならない
列挙の継承
列挙が型もopen指定することで継承可能(open enum)
open enum class OptionKeys { OPTION1 } enum class ExtraOptionKeys : OptionKeys { OPTION2 } fun demo() { ExtraOptionKeys.OPTION2 : ExtraOptionKeys // legal ExtraOptionKeys.OPTION1 : OptionKeys // legal // OptionKeys.OPTION2 はエラー }
抽象クラス
abstract にて宣言する
abstract class A { abstract fun f() } trait B { open fun f() { print("B") } } class C() : A(), B { }
trait で実装することもできる
プロパティのオーバーライド
open class Base { open val p : Int get() = 1 } class Derived : Base() { override val p : Int get() = 2 }
個別にオーバーライドするには
open class Base { var p : Int get() = 1 open set(value) { ・・・ } } class Derived : Base() { var p : Int override set(value) { print(value) } }
委譲
他クラスへの Delegation が簡単に書ける
trait Base { fun print() } class BaseImpl(val x : Int) : Base { override fun print() { print(x) } } class Derived(b : Base) : Base by b fun main() { val b = BaseImpl(10) Derived(b).print() // prints 10 }
by にてデリゲート指定できる
宣言サイトバリアンス
abstract class Source<out T> { fun nextT() : T } fun demo(strs : Source<String>) { val objects : Source<Any> = strs // ... }
- out パラメータなので val objects : Source
= strs は有効
abstract class Comparable<in T> { fun compareTo(other : T) : Int } fun demo(x : Comparable<Number>) { x.compareTo(1.0) val y : Comparable<Double> = x }
- inパラメータなので、Number のサブ型である Doubleが扱える
型射影
Javaでの Array は out を指定して以下のように定義する
fun copy(from : Array<out Any>, to : Array<Any>) { // ... }
Javaでの Array は in を指定して以下のように定義する
fun fill(dest : Array<in String>, value : String) { // ... }