読者です 読者をやめる 読者になる 読者になる

初めてのコトリン 〜9分ぐらいで文法編〜 その2

Kotlin

以下をベースに文法をザックリまとめ。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

デフォルト引数

関数の引数にはデフォルト値を指定できる

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) { 
  // ... 
}