Kotlin開発環境の構築
- IntelliJ IDEA Communityをダウンロード、インストール
- Projectを新規作成、以下にチェックを入れる
- Java
- Kotlin/JVM
Kotlin Playground で Web上でKotlinお試し実行する
Kotlin文法メモ Javaと異なる部分を主に書き出す
変数
-
var
・・・ 再代入可 -
val
・・・ 再代入不可 -
const val
・・・ 定数。トップレベルかつ基本型でなくてはならない。valとほとんど同じだが、定数はなるべくこちらを使う。
基本型
-
Boolean
・・・ true / false -
Char
・・・ 例) 'a' -
String
・・・ 例) "abc" - 数値型
-
Byte
Short
Float
Int
Long
Float
Double
・・・ 符号あり -
UByte
UShort
UInt
ULong
・・・ 符号なし
-
-
Any
・・・ すべてのクラスの親クラス(JavaのObject的な) -
Unit
・・・ 型が全くない(Javaのvoid的な) -
Nothing
・・・ 存在しない値。例外をスローする場合などに使われる。
Null許容型
-
var i: Int = 0
・・・ null非許容型 -
var j: Int? = null
・・・ null許容型。?をつける -
var i2: Int = i ?: -1
・・・ エルビス演算子。nullの時に右の値が採用される
列挙型
enum class Color {
Red, Green, Blue, Yellow
}
fun main() {
println(Color.Blue) // Blue
println(Color.Green.ordinal) // 1
println(Color.Blue.name) // Blue
}
型推論
-
var a = 1.23f
・・・ 型名を省略可
演算子
-
c =if (a > b) a else b
・・・ if式を三項演算子として使う。Javaの三項演算子は使えない。 -
==
・・・ 内容が同じか比較 -
===
・・・ 参照が同じか比較。同じオブジェクトかどうか
制御構文
- when式(enumであれば、else省略可)
fun main() {
val c = Color.Blue
when (c) {
Color.Red -> { println("あか")}
Color.Green -> { println("みどり")}
Color.Blue -> { println("あお")}
}
}
enum class Color {
Red, Green, Blue
}
-
for (i in 1..100) { }
・・・ for文
コレクション
リスト
-
listOf(1,2,3,)
・・・ イミュータブル(変更不可)List(末尾のカンマOK) -
mutableListOf(1,2,3,)
・・・ ミュータブル(変更可)List
var list = listOf(1,2,3,)
// list.add(4) コンパイルエラー
println(list) // [1, 2, 3]
var mutableList = mutableListOf(1,2,3,)
mutableList.add(4)
println(mutableList) // [1, 2, 3, 4]
セット(重複不可)
-
setOf(1,2,3,)
・・・ イミュータブル(変更不可)Set(末尾のカンマOK) -
mutableSetOf(1,2,3,)
・・・ ミュータブル(変更可)Set
マップ
-
mapOf(1 to "One",2 to "Two",3 to "Three",)
・・・ イミュータブル(変更不可)map(末尾のカンマOK) -
mutableMapOf(1 to "One",2 to "Two",3 to "Three",)
・・・ ミュータブル(変更可)map
var myMap = mutableMapOf(1 to "One",2 to "Two",3 to "Three",)
for (entry in myMap) {
println("${entry.key} ${entry.value}") // キーと値を取り出す
}
配列
-
arrayOf(1,2,3,)
・・・ 配列は参照型。アドレスを保持する。
println(arrayOf(1,2,3,)) // [Ljava.lang.Integer;@5d099f62
println(listOf(1,2,3,)) // [1, 2, 3]
シーケンス
-
sequenceOf(1,2,3,)
・・・ リストとほぼ同じように使えるが、.filter()などのステップコレクションは要素単位で最後まで実行する(リストはすべて要素が終わるまで次のステップへ進まない)。コレクション処理チェーンのパフォーマンスが上がる可能性がある。
関数
- 名前付き引数 ・・・ 関数を呼び出すときに引数=値で、引数を明示的に指定して呼び出す
- デフォルト引数 ・・・ 関数定義の引数=値で、値が指定されていない場合、値がデフォルト適用される
- インライン関数 ・・・ funの前に
inline
をつけると呼び出し元で関数の中身がインライン展開される
クラス
- クラスはデフォルトでfinal宣言となる
- 継承したいクラスは、open宣言する必要がある
- constructor()でコンストラクタ宣言できる。
- コンストラクタの引数にvar valをつけることによってプロパティにできる
- セカンダリーコンストラクタは、
constructor(age: Int) : this()
のようにthis()
でプライマリーコンストラクタを継承する必要がある。
- プロパティ
- varはget()、set()、valはget()を利用することでカスタマイズできる
- インナークラス宣言
class Outer {
var outerVersion = 0.0
class Inner {
var innerVersion = 1.4
fun setOuterVersion(version: Double) {
// outerVersion = version // コンパイルエラー(内部から外部の変数を参照できない)
}
}
fun setInnerVersion(version: Double) {
// innerVersion = version // コンパイルエラー(外部から内部の変数を参照できない)
}
}
class Outer {
var outerVersion = 0.0
inner class Inner {
var innerVersion = 1.4
fun setOuterVersion(version: Double) {
outerVersion = version // OK(inner classは内部から外部の変数を参照できる)
}
}
fun setInnerVersion(version: Double) {
//innerVersion = version // コンパイルエラー(外部から内部の変数を参照できない)
}
}
fun main() {
println(Outer().Inner().innerVersion) // 1.4が表示される
}
- シングルトンクラス
-
class
をobject
で宣言するとシングルトンクラスが作成できる - クラス全体をstatic的に使いたい場合
-
object MyClass {
val a = 100
fun printValue1() {
println(a)
}
}
fun main() {
MyClass.printValue1() // printValue1はobject内なのでインスタンス化する必要なし
}
- コンパニオンオブジェクト
- 一部のフィールドやメソッドをstatic的に使いたい場合
class MyClass {
companion object {
val a = 100
fun printValue1() {
println(a)
}
}
fun printValue2() {
println(a)
}
}
fun main() {
MyClass.printValue1() // printValue1はcompanion object内なのでインスタンス化する必要なし
MyClass().printValue2() // printValue2はcompanion object外なのでインスタンス化する必要あり
}
- 抽象クラス
-
abstract class
宣言する - 抽象クラスには状態があり、インターフェースには状態がない
-
abstract class abstractClass {
abstract val a: Int
abstract fun printValue(): Unit
}
class MyClass : abstractClass() { // 実装クラス名 : コンストラクタ呼び出し
override val a = 123
override fun printValue() {
println(a)
}
}
fun main() {
MyClass().printValue()
}
データクラスとシールドクラス
- データクラスは
data class
で宣言する- 継承はできない
- 自動的に
equals()
hashCode()
toString()
などが生成される -
equals()
の代わりに、==
で比較できる
data class Person(val name: String, val age: Int)
fun main() {
val a = Person("ichiro", 22)
val b = Person("ichiro", 22)
val c = b
println(a == b) // true
println(a.equals(b)) // true
println(a === b) // false
println(b === c) // true
}
- シールドクラス
-
sealed class
で宣言する - 制限されたクラス階層を表現できるクラス
- 柔軟性があるenum的な(enumにある、values() names() ValueOf()などは使えない)
-
sealed class State {
object Success: State() //
object Wait: State()
data class Warning(val warningCode: Int)
data class Exception(val exceptionMessage: String)
}
fun main() {
val s = State.Success
val w = State.Warning(234)
println(s == State.Success) // true
println(w.warningCode) // 234
}
インタフェース
interface Animal {
val name: String // 抽象化したプロパティ
fun greet(message: String) // 抽象化した関数
val age // プロパティのデフォルト実装
get() = 2
fun birth(year: Int) = year - age //関数のデフォルト実装
}
class Cat : Animal {
override val name : String // 実装プロパティ
get() {
return "Siamese"
}
override fun greet(message: String) { // 実装メソッド
println("$name $message")
}
}
fun main() {
val cat = Cat()
println(cat.name)
cat.greet("Hello")
println(cat.age)
println(cat.birth(2022))
}
- SAM(Single Abstract Method)インタフェース
- メソッドを1つしか持たないインターフェース
-
fun interface
で宣言する - デフォルト実装は使えない
interface Culculator1 {
fun times(x: Int): Int
}
// SAMインタフェースは fun をつける(メソッドは1つのみ)
fun interface Culculator2 {
fun times(x: Int): Int
}
fun main() {
// Kotlin1.3の実装方法
val result1 = object : Culculator1 {
override fun times(x: Int) = x * 3
}
println(result1.times(10))
// Kotlin1.4以上のSAMインターフェースは、インターフェース名に実装でき、ラムダ式を使える
val result2 = Culculator2 { it * 4 }
println(result2.times(10))
}
継承
- すべてのクラスにAnyが継承され、equals() hashCode() toString()などが定義されている
- 継承するには継承元クラスにはopenをつける必要がある
- 「: 継承元コンスタントラクタ」
- 継承元関数にもopenする必要がある
open class Language {
open fun hello() {
println("Hello!")
}
}
class Kotlin : Language() {
override fun hello() {
println("Kotlin!")
}
}
fun main() {
val kotlin = Kotlin()
kotlin.hello()
}
- 抽象化クラス
- デフォルト実装は使用できない
abstract class Language {
abstract fun hello()
}
class Kotlin : Language() {
override fun hello() {
println("Kotlin!")
}
}
fun main() {
val kotlin = Kotlin()
kotlin.hello()
}
例外処理
-
Throwable
はすべてのエラーもしくは例外の基本的なクラス - Kotlinには検査例外はない
- tryは式であるため、結果を受け取ることができる
throw Throwable("例外!") // Exception in thread "main" java.lang.Throwable: 例外!
try {
throw Throwable("例外!")
} catch (e: Throwable) {
println("例外が発生しました")
e.printStackTrace()
} finally {
println("処理終了")
}
スマートキャスト
- 型のチェックをすることで自動的かつ安全にキャストする
-
x is Int
でチェックすると、スコープ内ではxはInt型とみなされる
val x: Any = 43
if (x is Int) {
println(x.plus(10)) // 53 xはInt型とみなす
}
スコープ関数
-
let
- コンテキストオブジェクト
it
- 戻り値=ラムダの実行結果
- 拡張関数
- コンテキストオブジェクト
-
run
- コンテキストオブジェクト
this
- 戻り値=ラムダの実行結果
- 拡張関数
- コンテキストオブジェクト
-
with
- コンテキストオブジェクト
this
- 戻り値=ラムダの実行結果
- 拡張関数ではない
- コンテキストオブジェクト
-
apply
- コンテキストオブジェクト
this
- 戻り値=コンテキストオブジェクト
- 拡張関数
- コンテキストオブジェクト
-
also
- コンテキストオブジェクト
it
- 戻り値=コンテキストオブジェクト
- 拡張関数
- コンテキストオブジェクト
ラムダとクロージャー
- ラムダは無名関数と比べて、
return
が利用不可、名前付き引数が利用不可
val anonymous = fun() {} // 無名関数
anonymous() // 無名関数の実行
val lambda = {} // ラムダ
lambda() // ラムダの実行
val lambda = {} // ラムダ
lambda() // ラムダの実行
val lambda2 = {name: String -> "$name lambda"} // 引数 -> 処理
println(lambda2("test method")) // test method lambda
- コレクションで使用するラムダ
val list = listOf(1, 2, 3, 4,)
println(list.map{it * 2}) // [2, 4, 6, 8]
println(list.filter{it % 2 == 0}) // [2, 4]
println(list.any{it % 2 == 0}) // true
list.mapIndexed{ _, element -> print(element)} // 1234 使わないパラメータは「_」で省略できる
- クロージャ
- ラムダ式の中から外部のスコープで宣言された変数にアクセスでき、変更もできる
var i = 0
(1..10).filter{ it % 2 == 0 }.forEach{
i += it
}
println(i) // 30
ジェネリクス
- ジェネリクスは型パラメータを作成できる機能
- Any型はメソッド呼び出すときに強制的にキャストする必要があり、キャスト失敗の可能性がある
- 3つの関係性
- 不変・・・サブタイプの関係に無い
- 共変・・・サブタイプの関係にある
- 反変・・・スーパータイプの関係性にある(サブタイプを逆転した関係性にある)
アクセス修飾子
- pulic
- デフォルト
- どこからもアクセスできる
- internal
- 同じモジュール内でアクセスできる
- protected
- トップレベルで利用できない
- サブクラスからアクセスできる
- 同じクラスからアクセスできる
- private
- 宣言を含むファイル内でのみアクセスできる
- 指定したクラス内でのみアクセスできる
- 外部のクラスからアクセスできない
- 同じクラスからアクセスできる
拡張関数
- 拡張関数とはクラスに対して独自の関数を定義できる
- サードパーティのクラスに対しても関数を定義できる
- ピリオドで型と関数を連結する
- thisというレシーバーオブジェクトを利用して関数やプロパティにアクセスでき、thisを省略して関数やプロパティにアクセスすることもできる
- メンバー関数と同じ名前の拡張関数を定義した場合には、メンバー関数が優先される
- 既に定義されている拡張関数と同名の拡張関数を定義した場合、同一パッケージにある拡張関数が優先される
- プロパティも拡張プロパティとして定義できる
- レシーバーオブジェクトとしてgetterを利用できるが、setterは利用できない
- Null許容型に対しても拡張関数を定義できる
-
Any?.toString()
はNullの場合、文字列のnullを返す
-
分解宣言
- オブジェクトを分解して複数の変数として受け取ることができる
- componentN()をoperatorを使用してオーバーライドする
class MyClass(val a: Int, val b: Int) {
operator fun component1() : Int {
return a + b
}
operator fun component2() : Int {
return a * b
}
}
fun main() {
val (a, b) = MyClass(123, 456)
println(a) // 579
println(b) // 56088
val c = MyClass(123, 456) // 分解宣言なしの場合
println(c.component1()) // 579
}
- データクラスは自動的にcomponentN()が自動で生成されるので、定義なしで分解宣言できる
data class MyPairClass(val a: Int, val b: Int)
fun plusMinus(a: Int, b: Int) : MyPairClass {
return MyPairClass(a + b, a - b) // データクラスを使用して2つの値を返却する
}
fun main() {
val (plus, minus) = plusMinus(543, 210)
println(plus) // 753
println(minus) // 333
}
遅延初期化
-
by lazy
- 最初に変数やプロパティにアクセスした際に初期化される
- 型に制限なし
-
val
のみ - Null、Nullでないどちらも可能
- 初期化されたら次回以降は必ず同じ値を返す
-
lateinit
- クラスのプロパティ作成後に初期化
-
var
のみ - Nullでない型
- 基本型を許可していない
- 抽象化クラスの抽象プロパティやsetter、getterを持つプロパティにも許可されていない
Type Alias
-
typealias
で既存の型の代替名を定義することができる - トップレベルで宣言する必要がある
Delegated Properties
- 既存のクラスやプロパティに対して機能を簡単に委譲する仕組みが提供されている
-
by
を用いて利用できる -
ReadWriteProperty
とReadOnlyProperty
インターフェースが提供されている - getter、setterをオーバーライドできる
コメント
-
//
/* */
/** */
を利用できる - 関数や引数に対して、
[]
付きでコメントすることによって、関数や引数を参照できる