LoginSignup
1
0

More than 1 year has passed since last update.

Kotlin 学習メモ【基本編】

Last updated at Posted at 2022-09-05

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が表示される
}
  • シングルトンクラス
    • classobjectで宣言するとシングルトンクラスが作成できる
    • クラス全体を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 を用いて利用できる
  • ReadWritePropertyReadOnlyProperty インターフェースが提供されている
  • getter、setterをオーバーライドできる

コメント

  • // /* */ /** */を利用できる
  • 関数や引数に対して、 [] 付きでコメントすることによって、関数や引数を参照できる

参考

基礎からわかる Kotlin 富田健二

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0