kotlin 基本構文 – 型,リテラル,null許容型,型変換,配列,コレクション,定数

アウトプットによる記憶の定着の意味で超入門書をトレースして書き起こす。
超入門が終わったら中級書に着手。

変数と型宣言

変数は宣言して使う。構文がまんまDelphiみたい。
kotlinは行末セミコロン不要。
こんなところで主張しなくても良いのにと思う。


# kotlin
var name: String = "default value"
println(name) // default value

# Delphi
var name: String = "default value";
writeln(name); // default value

型は以下の通り。Javaみたいに全てはクラスです!とか言わない。
プリミティブはプリミティブ。

データ型 概要
Boolean 真偽
Byte 8bit整数
Short 16bit整数
Int 32bit整数
Long 64bit整数
Float 32bit浮動小数点
Double 64bit浮動小数点
Char 文字
String 文字列

静的型付けなので定義した型以外の値を代入できない。
下記はIntelliJ IDEA上でコンパイル前にエラーであることがわかる。


var num: Int
num = 100
num = "hoge" // Error:(5, 11) Kotlin: Type mismatch: inferred type is String but Int was expected

型推論の挙動は以下の通り。最初に代入した値の型が変数の型となり、
2回目以降の代入で異なる型を代入できない。
型推論のミスマッチはコンパイルしたときにわかる。


var num = 108
num = 100
num = "hoge" // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized

変数宣言時に初期値を省略することができるが、型と初期値を両方省略することはできない。
これは変数宣言時に型推論の情報を決めるという仕組みでそうなっている。


var num
num = 100 // Error:(3, 9) Kotlin: This variable must either have a type annotation or be initialized

どんな値も入る魔法の型が存在する。Any型


var name: Any
name = 100
name = "hoge"

数値リテラル

10進数、16進数、2進数、指数。普通。他の言語と同じ。


var name: Int
name = 105
name = 0xFF
name = 0b11
name = 1.234e+5

Rubyにもあったけども、桁表現ができる。
絶対使わないだろうけど、知ってたら知ってたで無用に使ってみたくなる。


var name: Int
name = 105_200_300
println(name) // 105200300

型サフィックス

数値リテラルのデフォルト型はInt、浮動小数点数リテラルのデフォルト型はDouble。
リテラルに型サフィックスを付けることで、型を変更できる。
型サフィックスはLong変数のL、Float型のFのみ。
絶対使わなそうなパターンが用意されていないところの割り切りが、妥当な感じがする。


var intval = 300
println(intval::class) // class kotlin.Int
var intval2 = 300L
println(intval2:class) // class kotlin.Double

var fval
fval = 3.14
println(fval::class) // class kotlin.Double
fval2 = 3.14F
println(fval2::class) // class kotlin.Float

文字列リテラル

文字列リテラルはダブルクォート1つで括るタイプと、ダブルクォート3つで括るタイプの2つ。
前者はPHP,Rubyの流れではなく、あくまでJava。後者はPHPのヒアドキュメント相当。


var msg = """
      コトリンコトリン。コトリンコトリン。
      複数行にわたって文字列をかける。
      コトリンコトリン。コトリンコトリン。
"""
println(msg)
//    コトリンコトリン。コトリンコトリン。
//    複数行にわたって文字列をかける。
//    コトリンコトリン。コトリンコトリン。

PHPでいつも気持ちが悪い、ヒアドキュメント内のインデントの空白問題。
バー(|)を書き.trimMargin()を呼ぶことで、コード上のインデントを有効として残したまま変数内のインデントを無視できる。


var msg = """
      |コトリンコトリン。コトリンコトリン。
      |複数行にわたって文字列をかける。
      |コトリンコトリン。コトリンコトリン。
""".trimMargin()
println(msg)
//コトリンコトリン。コトリンコトリン。
//複数行にわたって文字列をかける。
//コトリンコトリン。コトリンコトリン。

変数展開はPHPと同じように$を使う。


    var value = 100
    var msg2 = "This is value ${value}"
    var msg3 = "This is value $value"
    println(msg2) // This is value 100
    println(msg3) // This is value 100

PHPの場合、変数の後に文字列を続けても変数分を自動認識してくれたが、kotlinはしてくれない。
変数の後には空白を書くか${}で囲む必要がある。


    var value = 100
    var msg4 = "This is value $valuehoge" // Error:(13, 32) Kotlin: Unresolved reference: valuehoges

null許容型

kotlinはデフォルトでnullを不許可。
変数宣言のときに型の末尾に”?”を付けることでnullを許容できるようにできる。


var str:String = "hogehoge"
str = null // Error:(18, 11) Kotlin: Null can not be a value of a non-null type String
var str2:String? = "hogehoge"
str2 = null
println(str2) // null

null許容型の変数をnull不許可の変数に代入することはできない。
代入される側をAny型にしたとしてもN.G.。


var str:String = "hogehoge1"
var str2:String? = "fugafuga"
str = str2 // Error:(19, 11) Kotlin: Type mismatch: inferred type is String? but String was expected
var stra:Any = str2 // Error:(19, 20) Kotlin: Type mismatch: inferred type is String? but Any was expected

ぬるぽ防止構文 (Safe access operator)

Nullableな変数がnullだった場合は、その変数にアクセスするとぬるぽが起こる。
ぬるぽを徹底して排除するために、メンバにアクセスする際にセーフコール演算子を使う。
変数がnullであった場合、セーフコール演算子はnullを返し、nullでない場合に限りメンバを返す。


var str: String = "hogehoge"
println(str.length) // 8
var str: String? = null
println(str.length) // Error:(19, 16) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
var str2: String? = null
println(str2?.length) // null

普通にnullable型とnullを比較することが出来るので、if文でぬるぽ回避することもできる。


var str:String? = null
if (str != null) println(str.length)

左辺がnullであった場合に限り指定した右辺を返す演算子(?:)も積極的に使える。

var str:String? = null
println(str?.length ?:0) // 0

implicitな型変換

kotlinは基本的に暗黙の型変換が効かない。
Javaでは拡大方向の暗黙的型変換が許容されているがkotlinでは不可。
以下、Int型の変数をLomg型の変数に代入しようとしているがN.G.。


var x:Int = 100
var y:Long = x // Error:(4, 18) Kotlin: Type mismatch: inferred type is Int but Long was expected

変数宣言時の初期化の際にも暗黙の型変換が効かないので、
型サフィックスを付けて、変数の型と初期値を一致させる必要がある。


var m:Float = 1.5 // Error:(3, 19) Kotlin: The floating-point literal does not conform to the expected type Float
var m:Float = 1.5f
println(m) // 1.5

基本的に、というのは、整数リテラルについては暗黙の型変換が行われる。割り切り。


var i:Long = 105
var j:Short = 40
var k:Byte = 10

explicitな型変換

基本的にimplicitな型変換に頼らないで、explicitに型変換する方が良さそう。


var i = 10
var j:Long = i.toLong()

配列型

配列型はC++のジェネリックみたいに書く。
getter/setter,iterator,accessorが準備されていて使いやすい。
nullableも指定できる。


var ary:Array = arrayOf("1","2","3")
var ary2:Array = arrayOf("1","2",null)

intArrayOf()はArrayを返すので、型推論のヒントとして与えることができる。


var ary3 = intArrayOf(1,2,3)

Ruby風に、配列生成の初期値としてラムダ式を与えることができる。
Arrayの第1引数は配列のサイズ、第2引数はラムダ式。
第2引数のラムダ式の中に現れる変数iはラムダに渡される変数であってiでなくても何でも良い。
ラムダと高階関数は後で出てくるのでそこで。


var data = Array(5, {i -> i*3 })
data.forEach{ println(it) } // 0 3 6 9 12

配列のアクセサは[...]演算子を使う。
範囲外アクセスは実行時例外。


var data = Array(5, {i -> i*3 })
println(data[0]) // 0
println(data[1]) // 3
println(data[10]) // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10

コレクション

arrayの他に、list,set,mapなどがある。


var list:List = listOf("hoge","fuga")
println(list) // [hoge, fuga]
var list2 = listOf("hoge","fuga")
println(list2) // [hoge, fuga]

var set:Set = setOf("A","B","C")
println(set) // [A, B, C]
var set2 = setOf("A","B","C")
println(set2) // [A, B, C]

var map = mapOf("first" to 1, "second" to 2, "third" to 3)
println(map) // {first=1, second=2, third=3}

定数

kotlinの定数はval予約語により定義する。
定数への再代入は不可。コンパイル前にIntteliJ IDEAでエラーがわかる。


val constant = "THIS IS CONSTANT VALUE"
println(val) // THIS IS CONSTANT VALUE
val = "RE SET VALUE" // Error:(18, 5) Kotlin: Unresolved reference: constanct

要はfinalなので、変数自体は変更できないが、変数の中身は変更できる。
例えば、配列を定数として定義したとき、配列自体を変更できないが、中身を変更できる。


val ary:array = arrayOf(1,2,3)
ary = arrayOf(10,20,30) // Error:(19, 5) Kotlin: Val cannot be reassigned
ary[0] = 100
println(ary[0]) // 10