この独学Kotlin入門では、Android Studioの『Kotlin REPL』の機能を使用します。Android Studioのインストールまでの流れはこちらにまとめています。
Kotlin REPLの起動
まずは、Kotlin REPLの起動を行います。Android Studioを起動して『New Project→No Activity』を選び、そのままの状態で『Finish』まで進んでください。
『New Project』を選択
『No Activity』を選択(ちなみにどれを選んでもOKです)
そのままの状態で『Finish』をクリックしてください
Tools→Kotlin→Kotlin REPLを選択します。
初回の起動時、おそらく次のようなエラーが出ると思います。
CommandLineWrapper is ill-suited for launching apps on Java 9+.If the run configuration uses “classpath file”, please change it to “@argfile”.Otherwise, please contact support.
世界中の方々が悩んでいるようですが気にせずに進みます。もう1度、Tools→Kotlin→Kotlin REPLを選択します。
次のようなWindowが出るので、1番上の『MyApplication』をクリックしてください。プロジェクトの作成で違う名前に変更された方は別の名前かもしれませんが、1番上を選択してください。
Kotlin REPLが起動します。
とりあえず、定番の『Hello World』を表示してみます。次のコードを入力してください。
println("Hello World")
『Ctrl + Enter』もしくは左の『▶』(Execute Kotlin Code)をクリックしてください。コードが実行されます。
このKotlin REPLを使用してこの独学Kotlin入門は進めていきます。
四則演算(二項演算子)
『二項演算子』というと難しく感じるかもしれませんが、ここでご紹介するのは小学生で習う四則演算の記号です。
演算子 | 意味 | 例 |
+ | 足し算 | a = 1 + 3(解:4) |
– | 引き算 | b = 5 – 2(解:3) |
* | かけ算 | c = 3 * 3(解:9) |
/ | 割り算の商 | d = 15 / 3(解:5) |
% | 割り算のあまり | e = 9 % 2(解:1) |
数学の基本になりますが、次のルールはプログラミングでも同じです。
・足し算と引き算よりもかけ算や割り算を先にする
・()の計算は最優先
// 『//』の右側はコメントで実行されないのでメモを書けます
a = 1 + 3 // 4
b = 5 % 2 // 1
y = 1 + 3 * 5 //16
z = (1 + 2) * 3 // 9
Kotlin REPLを使って次の計算を行ってみてください。aを『変数』と呼び、数値や文字を入れる箱のようなものです。
var a // 変数aを宣言
a = (3 + 2) * 5
print(a) //25
・varはまずは特に考えずに付けてください
・printというのは表示をするための関数です
・入力が終わったら『Ctrl + Enter』を押して下さい
Kotlin REPLで計算されて『25』という値が表示されました。数値を変えたり、演算子を変えていろいろとテストしてみてください。
代入
前回の項で、四則演算を行いました。次のようなコードを書きました。
var a = 1 + 3 //『var』はとりあえず付けておいてください
このような表記は理解しやすいかと思います。しかし、こちらはどうでしょうか? Kotlinだけでなく他のプログラミング言語でもこのような書き方をします。
a = a + 1
プログラミングを初めて学習する方は違和感を感じたのではないでしょうか? 数学では、イコールの左と右は必ず等しくなるので、この式は数学では成り立ちません。
これは、a+1した値をaに代入するということを表し、別の言葉で表現をすると「aに1を足す」という意味です。
実際にKotlin REPLを使用して確認してみましょう。
var a = 0 //変数aを宣言して初期値は0
a = a + 1 //aに+1した値をaに代入する(aに+1する)
print(a)
プログラミングでは、『=』は代入を表します。数学のイコールと同じ「等しい」を表すのは『==』とイコールを2個並べた記号を使用します。
var a = 0 // 変数aを宣言し初期値は5
a = 5 // aに5を代入する(aは5になった)
a == 5 // true(数学の『=』と同じ)
valとvarの違い
Kotlinでは変数を『var』と『val』で宣言します。varとvalの違いは後から値を変更できるか、できないかの違いです。
var:値の変更が可能
val:値の変更が不可
実際の動きをKotlin REPLで確認してみます。
varで定義したnumは変更可能です
valで宣言したnumを変更しようとするとエラーが発生します
この動きを見ると柔軟なvarに比べて変更できないvalは不便に感じるかもしれません。しかし、この書き換えできないということが、安定したプログラミングを行うためには非常に重要です。サクサクと書き換えが可能なコードは思わぬバグを招く可能性が高まるからです。できるだけvalで変数は宣言するようにしてください。
ちなみにvarのように変更ができることをミュータブル、valのように変更できないことをイミュータブルと呼びます。Kotlinではミュータブル、イミュータブルという表現を頻繁に使うので、覚えにくい言葉ですがしっかりと覚えておいてください。
データ型
Kotlinでは様々な『データ型』があります。よく使われるものをご紹介します。最初のうちは整数、小数などだけを覚えておき、bitなどは後から覚えれば大丈夫です。
データ型 | 特徴 | 例 |
Byte | 整数型(8bit) | 5 |
Short | 整数型(16bit) | 31234 |
Int | 整数(32bit) | 2147483647 |
Long | 整数(64bit) | 2000125563 |
Float | 浮動小数点型(32bit) | 123.3 |
Double | 浮動小数点型(64bit) | 512.456 |
Boolean | ブール型 | true, false |
Char | 文字 | ‘c’ |
String | 文字列 | “taro” |
・文字型のCharは『’(シングルクォート)』で囲みます
・文字列型のStringは『”』ダブルクォートで囲みます
データ型をそろえないと代入したり計算することができないので注意してください。
var num1:Int = 1
var num2:Int = 2
var sum:Int
sum = num1 + num2
print (sum) // 3
var num1:Int = 1
var num2:Long = 2
var sum:Int
sum = num1 + num2
print (sum) // エラー
Int型とLong型のように違うデータ型を足そうとするとエラーになります
データ型を変換する
前回、異なるデータ型では代入したり、計算することができないとご紹介しました。しかし、データ型の変換を行うことで異なるデータ型でも代入・計算などを行うことが可能です。
val num1:Int = 1
val num2:Long = 2
var sum:Int
sum = num1 + num2.toInt()
print(sum) // 3
このデータの変換はInt型からLong型のような数値だけではなく数値を文字列に変換することも可能です。
//文字列にInt型を足す
val str1:String = "taro"
val num:Int = 1
var str2:String
str2 = str1 + num.toString()
print(str2) // taro1
このデータ型の変換を活用すると次のようなこともできます。
//シンプルにInt型の足し算
val num1:Int = 1
val num2:Int = 2
var sum:Int
sum = num1 + num2
print(sum) // 3
//Int型で宣言した変数を文字列型に変換して足し算
val num1:Int = 1
val num2:Int = 2
var sum:String
sum = num1.toString() + num2.toString()
print(sum) // 12
型推論
Kotlinでは『型推論』という機能があります。Kotlinでは次の2つの方法で変数を宣言することができます。
val a:Int = 1 //変数aはInt型の変数と明記して宣言する方法
val a = 1 //Int型であることは省略して宣言することもできる
この時、変数aの中身である1をリテラルと呼びますが、リテラルからデータ型を自動で推測してくれます。これが型推論です。型推論を利用するとコードの量を減らすことができて便利です。
しかし、Long型で初期値を0と宣言したい場合、ただ0と書いただけではInt型と推論されてしまいます。Long型で宣言したい時は、テクニックが必要です。末尾に注目してください。
val num = 0 // Int型
val num = 0L // Long型
val num = 0F // Float型
val num = 0.0 // Double型
val char = 'a' // Char型
val name = "taro" // String
ちなみに『変数名.javaClass.kotlin』という方法で変数の型を確認することができます。
Int型と推論
Float型と推論
String型と推論
文字列連結(文字列テンプレート${})
Kotlinでは『文字列テンプレート』の機能を活用することで、文字列の中にInt型やLong型などの数値や別の文字列をはさみこむことができます。
${変数} を文字列にはさみこみます
『${}』の記号を見ると「複雑そう。難しそうだな」と感じる方もいるかもしれませんが、実際のコードで確認してみると何も難しいことはありません。
実際に文字列連結を確認してみます。
val age = 35
val name = "taro"
val intro = "My name is ${name}, ${age} years old."
print(intro)
配列
プログラミングでは、1つの変数に1つの値を格納するだけではなく、『配列』という方法でまとめてデータを格納しておくことができます。もちろん、Kotlinも同様で『arrayOf』という関数を使用して配列を生成します。
実際を使ってみます。
//インデックス 0 1 2
var day = arrayOf("Monday", "Tuesday", "Wednesday")
println(day[1]) //Tuesday
格納されたMondayなどの値を『要素』、day[1]の1のような配列の番号を表す数値を『インデックス』と呼びます。今回の例の場合は以下の値が格納されています。
day[0] = "Monday" //インデックス= 0 / 要素:Monday
day[1] = "Tuesday" //インデックス= 1 / 要素:Tuesday
day[2] = "Wednesday" //インデックス= 2 / 要素:Wednesday
さらに要素を追加したい時には以下のようにします。
//インデックス 0 1 2
var day = arrayOf("Monday", "Tuesday", "Wednesday")
day += "Thursday" //要素の追加
println(day[3]) //Thursday
変数dayをここまでミュータブル(変更可能)のvarで宣言していますが、イミュータブル(変更不可)のvalで宣言して要素を追加するとどうなるでしょうか?テストしてみます。
//インデックス 0 1 2
val day = arrayOf("Monday", "Tuesday", "Wednesday")
day += "Thursday"
println(day[3]) //正常に出力される?
valは書き換えができない変数の宣言だったのでエラーが発生しました。これを不便と感じるかもしれませんが、以前の項でもご紹介しましたが、valの書き換えができないというのはプログラムの安定動作を図るためには非常に重要です。できるだけvalを使用することを意識してください。
コレクション
Kotlinでは、複数のデータをまとめて扱うための機能を『コレクション』と呼びます。コレクションは次の3つです。
・リスト(List)
・セット(Set)
・マップ(Map)
前回、ご紹介した配列も要素をまとめておくという意味ではコレクションに似ていますが、Kotlinではこの3つをコレクションと呼びます。さらにこの3つも、ミュータブル(変更可能)とイミュータブル(変更不可)の2タイプがあります。
厳密にはイミュータブルなコレクションも変更することはできますが、今回は簡略化させて頂きます。
List
Listは次のような特徴があります。
・配列と同様に生成した時の順番を保持する
・要素を登録した順番にインデックスが割り振られる
・インデックスから要素にアクセスすることができる
・要素が重複しても別のデータとして格納できる
実際にListの動きを確認してみます。まずはイミュータブル(変更不可)なListの生成方法です。
//イミュータブル(変更不可)なListを生成
val list:List<Int> = listOf<Int>(10,20,10,30)
print(list) // [10, 20, 10, 30]
要素にアクセスするには配列と同じように追加した順番に割り振られるインデックスを利用します。『get()』というメソッドを活用してもOKです。メソッドは後の項のクラスのところでご紹介します。
// list[n] = インデックスnの要素
// list.get(n) = 〃
//インデックス // 0 1 2 3
val list:List<Int> = listOf<Int>(10,20,10,30)
print (list[2]) // 10
print (list.get(2)) // 10
イミュータブル(変更不可)で生成したListは要素を追加することができません。addは要素を追加するためのメソッドです。
//イミュータブル(変更不可)なListを生成
val list:List<Int> = listOf<Int>(10,20,10,30)
list += 40 //エラー
list.add(40) //エラー
ミュータブル(変更可能)なListを生成すると次の処理(メソッド)を使って要素の追加や削除もできます。
要素の追加:add()
要素の削除:remove() ※要素を指定して削除
要素の削除:removeAt() ※インデックスを指定して削除
//ミュータブル(変更可能)なListを生成
val list:MutableList<Int> = mutableListOf<Int>(10,20,10,30)
list.add(40) // 要素を追加
print (list[4]) // 40
//ミュータブル(変更可能)なListを生成
val list:MutableList<Int> = mutableListOf<Int>(10,20,10,30)
list.remove(20) //要素の20を削除する(要素を指定)
print(list) // [10, 10, 30]
//ミュータブル(変更可能)なListを生成
val list:MutableList<Int> = mutableListOf<Int>(10,20,10,30)
list.removeAt(2) //インデックス[2]の要素を削除する(インデックスを指定)
print(list) // [10, 20, 30]
Set
次はコレクションの1つであるSet(セット)をご紹介します。SetはListとかなり違った個性を持っています。次の3点を意識するとListとの違いを理解しやすいかと思います。
・要素の並びが追加された順とは限らない
・要素の重複が禁止
・基本的にはインデックスからアクセスできない
Listと同じようにSetにもミュータブル(変更可能)とイミュータブル(変更不可)があります。まずは、イミュータブルなSetを使ってみます。
//イミュータブル(変更不可)のSetを生成
val numSet:Set<Int> = setOf(10,20,20,30) //20の要素が2つある
print(numSet) // [10, 20, 30]
重複していた要素(20)が1つにまとめられています。またListと同様ですが、イミュータブルで生成したSetに要素を追加することができません。
//イミュータブル(変更不可)のSetを生成
val numSet:Set<Int> = setOf(10,20,30)
numSet.add(40) // エラーが発生(要素を追加できない)
次にミュータブル(変更可能)なSetの動きを確認してみます。Listと同様に
要素の追加:add()
要素の削除:remove()
こちらのメソッドを利用して要素の追加・削除を行うことができます。
//ミュータブル(変更可能)のSetを生成
val numSet:MutableSet<Int> = mutableSetOf(10,20,30)
numSet.add(40) // 40を追加
numSet.remove(10) // 10を削除
print(numSet) // [20,30,40]
Setで注意すべき点があります。この項の最初にご紹介しましたが、基本的にはインデックスから要素にアクセスすることができないという点です。Listなら次のような方法でインデックスから要素を扱うことができました。
//Listの場合はインデックスから要素にアクセスできる
list[2] // インデックス[2]の要素にアクセス
list.get(2) // 〃
しかし、Setはインデックスから要素にアクセスすることができません。
//ミュータブル(変更可能)のSetを生成
val numSet:MutableSet<Int> = mutableSetOf(10,20,30)
print(numSet.get(2)) // エラー。Listのようにインデックスから要素を取得できない
print(numSet(1)) // エラー。 〃
プログラミングを学習し始めた方の中には「Setは扱いづらい」と感じるかもしれませんが、まずはSetとListの違いを理解しつつ、少しずつ慣れていけば良いかと思います。
Map
コレクションの3つ目のMapは、ListやSetとまた異なる特徴があります。Pythonを学習したことがある方なら『dict(辞書型)』を連想してもらえればと思います。Map型は次のような特徴があります。
・インデックスではなくキーで要素を扱うコレクション
Listはインデックスと呼ばれる番号で対応する要素を呼び出して扱っていました。Mapは数値だけのインデックスではなく『キー』を利用して要素を呼び出します。
price[2] = 100 //List
price["apple"] = 100 //Map
全く説明しなくてもコードから「りんごの値段だな」と推測できるかと思います。まずは、イミュータブル(変更不可)なMapから動きを見てみます。
//イミュータブル(変更不可)なMapを生成
//キー:String型 / 要素:Int型の例
val price:Map <String,Int> = mapOf("apple" to 100, "grape" to 500, "melon" to 1000)
print(price["grape"]) //500
//右が切れたらスクロールしてください
次はキーも要素もString型のMapを試してみます。
//イミュータブル(変更不可)なMapを生成
//キー:String型 / 要素:String型の例
val price:Map <String,String> = mapOf("apple" to "Red", "grape" to "Purple", "melon" to "Green")
print(price["grape"]) //Purpe
//右が切れたらスクロールしてください
ListやSetでもご紹介しましたが、イミュータブル(変更不可)で生成したコレクションに要素の追加・削除はできません。Mapも同様です。
//イミュータブル(変更不可)なMapを生成
val price:Map<String,Int> = mapOf("apple" to 100, "grape" to 500, "melon" to 1000)
price.put("plum",50) //エラー ※put()はMapで要素を追加するためのメソッド
print(price["plum"])
ミュータブル(変更可能)なMapを生成するとメソッドを使用して要素を追加・削除することができます。
要素の追加:put(key, 要素)
要素の削除:remove(key)
要素の削除:remove(key, 要素)
実際に動きを確認してみます。
//ミュータブル(変更可能)なMapを生成
val price:MutableMap<String,Int> = mutableMapOf("apple" to 100, "grape" to 500)
price.put("plum",50) //put() で要素を追加
price.remove("apple") //要素を削除(キーのみを渡す)
price.remove("grape", 500) //要素を削除(キーと要素を渡す)
print(price["plum"])
次のような方法でもMapの要素を追加することができます。
//ミュータブル(変更可能)なMapを生成
val price:MutableMap<String,Int> = mutableMapOf("apple" to 100, "grape" to 500)
price += mapOf("plum" to 50) //こんな書き方でも要素を追加できる
print(price["plum"]) //50
Mapを利用すると初めてコードを確認する人でも理解しやすいコードを書くことができます。ぜひ積極的に活用してみてください。
比較演算子と論理演算子
プログラミングでは条件で分岐をもうけて複雑な処理を行います。
分岐の条件で使用されるのが『比較演算子』と『論理演算子』です。言葉は難しそうですが、なじみがある記号なので始めての方でもすぐに意味を理解できるかと思います。よく使用されるものをここではご紹介します。
その条件を満たしている時に『true』と判断されます。満たさない時には『false』が返されます。
比較演算子
比較演算子 | 結果 |
!A | Aが条件を満たさない時にtrue |
A == B | AとBが等しい時にtrue |
A != B | AとBが異なる時にtrue |
A > B | AがBより大きい時にtrue |
A < B | AがBより小さい時にtrue |
A >= B | AがB以上(Bと同じも含む)の時にtrue |
A <= B | AがB以下(Bと同じも含む)の時にtrue |
A === B | AとBが等しくデータ型も同じ時にtrue |
論理演算子
論理演算子 | 結果 |
A && B A and B |
AもBも条件を満たす時ならばture |
A || B A or B |
AかBのどちらか、もしくは両方が条件を |
A not B | AとBが異なる時にtrue |
if文(if式)
他のプログラミング言語でも使用されるif文ですが、Kotlinは『式』としても使用できるという特徴があります。まずはシンプルに文として使った例をご紹介します。
//条件を満たしたときは処理1、それ以外は処理2を実行
if (条件) {
処理1
} else {
処理2
}
実際に動きを見てみます。
//身長(hight)が170cmを超えるならL、それ以外ならMと表示
val hight = 165
if (hight > 170) {
print("L")
} else {
print("M")
}
次はさらに複雑にしてみます。『else if』を使うと分岐をいくつでも追加することが可能です。
//175cm超えならLL、170cm超えならL、それ以外ならMと表示
val hight = 176
if (hight > 175) {
print("LL")
} else if (hight > 170) {
print("L")
} else {
print("M")
}
この項の始めにKotlinでは、if文は『式』として扱うことができるとご紹介しました。次のような使い方です。
//hightが170超えならL、それ以外ならMをsizeに代入する
val hight = 165
val size = if (hight > 170) {
"L"
} else {
"M"
}
print(size) //M
このようにKotlinでは条件分岐の『文』としてだけではなく、『式』として利用することで記述するコードの量を減らすことができます。
when式
Kotlinでは分岐の方法に『when式』があります。when式はJavaやC言語に慣れている方なら『switch文』を連想してもらうとイメージしやすいかと思います。ただし、swtich文で必要だったbreakは不要です。
//when式の基本的な使い方
when(value){
a -> 処理1 //value = a ならば処理1を実行
b -> 処理2 //value = b ならば処理2を実行
・
・
・
else -> 処理3 //上記の条件に一致しなければ処理3を実行
}
実際にwhen式を使ってみます。
val num = 1
when (num) // numの値を条件にする
{
0 -> print("apple") // num = 0の時はappleを表示
1 -> print("banana") // num = 1の時はbananaを表示
2 -> print("melon") // num = 2の時はmelonを表示
else -> print("else")
}
ifと同様に、whenも文ではなく『式』なので、次のような使い方もできます。
//numの値によって分岐してfruitに文字列を代入する
val num = 1
val fruit = when (num) {
0 -> "apple"
1 -> "banana"
2 -> "melon"
else -> "else"
}
print(fruit) // banana
when式を活用するとすっきりとコードを書くことができる場合も多いです。ifだけではなく、ぜひwhen式も活用してください。
range
Kotlinでは、数値の範囲を表現する『range』という方法があります。次のように『..』とドット2個で2つの数値をつなげると、IntRange型と呼ばれる範囲を表現する型になります。Intなので1刻みの整数になります。
val i = 0..5 //0~5(1刻み)
if式やwhen式の条件として活用することもできます。実際に使用してみます。
//when式でrangeを使ってみる
val num = 1
val fruit = when (num) {
in 0..2 -> "apple" // num が 0,1,2 なら"apple"
else -> "else"
}
print(fruit) // apple
if式でrangeを使うと次のようになります。
//if式でrangeを使ってみる
val num = 1
val fruit = if (num in 0..2) { // num in a..b の形。in をお忘れなく
"apple" // num が 0,1,2 なら"apple"
} else {
"else"
}
print(fruit) // apple
for文
次のようなコードがあったとします。
//0から3までを1刻みで表示(改行はしない)
print(0)
print(1)
print(2)
print(3)
このような書き方は良くないコードの典型的な例です。何度も同じこと(ここではprint)を続けて書く書き方は後ほど解説しますが良くないコードの代表です。くり返しを行う時には、for文や次にご紹介するwhile文を使用するのが一般的です。まずはfor文からご紹介します。
//rangeを使用したfor文
for(i in 0..3) { //0から3までの範囲でtrue(ループをくり返す)
print(i) //実行結果:0123
}
実行結果は先ほどと同じです。「結果が同じなら先ほどの書き方で良いのでは?」と感じる方もおられるかもしれませんが、この0~3を0~1000までに変更する必要が生じた時、最初の書き方では非常に大変ですが、for文なら次のように変更するだけで簡単です。
for(i in 0..1000) { //3を1000に変更するだけ
print(i)
}
余談ですが、ここでfor文のくり返しを制御するために『i』を使用しました。これは『カウンタ』と呼ばれます。カウンタは自由に何を使用しても良いですが、基本的にはiを使用することをおすすめします。理由は他のプログラミング言語も同様ですが
カウンタはi, j, kの順番に使用する
と暗黙のうちに決められているので、違う人が見た時に「これはカウンタだな」と理解しやすいためです。特別な理由がない場合はカウンタには、i、j、kの順に使用することをおすすめします。
while文
前回のfor文と同様に、くり返しで有名な『while文』もKotlinで使用することができます。
//while文の使い方
while (条件) {
くり返す処理
}
実際の動きを確認してみます。
var i = 0
while(i < 5) { // ループの条件はiが5未満の時
print(i)
i++ // iを+1する(インクリメント)
}
while文は『do-while』という使い方も有名です。書き方は次の通りです。
do {
くり返す処理
} while (条件)
do-whileは、whileの実行条件を満たしていなくても1度は実行するというルールになっています。まず比較用でwhile文(条件を満たさない時)の動きを見てみます。
// while文の動作(条件を満たさない時)
var i = 6
while (i < 5) { // iが5未満ならループ
print(i)
i++ // iに+1をする
}
くり返しの条件を満たさないのでwhile文では何も表示されません。次にdo-while文で同じループ条件の処理を行ってみます。
// do-while文の動作(条件を満たさない時)
var i = 6
do {
print(i)
i++ // iに+1をする
} while (i < 5) // iが5未満ならループ
do-while文はくり返しの条件を満たしていなくても、1度はdoの処理を実行していることが分かります。
関数
Kotlinでは、Javaとは異なり、C言語のようにクラスに関係なく自由に関数を作成することが可能です。関数を実行するために渡す変数を『引数』、関数で処理されて返ってきた値を『戻り値』と呼びます。
//funは『function(関数)』の意味
fun 関数名(引数1:データ型, 引数2:データ型・・・): 戻り値のデータ型 {
処理
}
実際に簡単な関数を作成して動きを確認してみます。2つの引数を与えてかけ算をして返す『mult()』を作成してみます。(かけ算:Multiplication)
//mult関数を宣言
fun mult (x:Int, y:Int):Int {
return x * y
}
//実際にmult関数を使用してみる
val a = 3
val b = 4
val result = mult(a, b)
print(result) // 12
ラムダ式
『ラムダ式』などと聞くと「なんかすごく難しそう」と感じるかもしれませんが、実は簡単に関数を使うための書き方です。前回の関数の項では、こんな関数を作成しました。
//関数の書き方
fun mult (x:Int, y:Int):Int {
return x * y
}
ラムダ式は『->』を使用して次のように書きます。
//ラムダ式の書き方
var mult = {x:Int, y:Int -> x * y} //1行ですっきりと書けます
val a = 3
val b = 4
val result = mult(a, b)
print(result) // 12
気づいた方もおられるかもしれませんが、ラムダ式は『fun(関数)』ではなく、varでmultを宣言しています。そのため、ラムダ式のmultは関数ではなく関数型の変数という扱いになります。Int型の変数を2つもらってInt型の戻り値を返すので、複雑そうに見えますがmultはInt型の変数になります。
multのデータ型を確認してみます。
ご紹介したようにInt型になっていることが分かります。
次はString型の引数を1つだけ渡すラムダ式をテストしてみます。
//String型の引数を1つ渡すラムダ式
val intro = {name:String -> "My name is ${name}"} //${name} 文字列連結
val name = "taro"
print(intro(name)) //My name is taro
ラムダ式とit
この部分は初心者の方には難しい部分になるので、一旦飛ばして後から理解を深めることをおすすめします。
前回のラムダ式で式を宣言しました。これは関数型の変数とも呼ぶのでした。
//String型の引数を1つ渡すラムダ式
val intro = {name:String -> "My name is ${name}"} //${name} 文字列連結
ラムダ式の引数が1つの場合は『it』を使用すると次のように書くことができます。実際のコードで動きを確認してみます。
val intro :(String)->String = {"My name is ${it}"}
val name = "taro"
print(intro(name)) //My name is taro
結果は先ほどと同じです。ちょっと理解が難しい部分になりますが、ラムダ式は引数が1つの場合は暗黙のうちに引数はitとして引き渡されるという特徴があります。そのため、このような記述を行うことができます。
オブジェクト指向とは(クラス)
オブジェクト指向とは、どのサイトや教本でも語られていますが、部品で組み立てるようにしてプログラミングを行うことです。これだけ聞いても「どういう意味?」と感じる方もおられるかと思いますが、この項と次のインスタンス化を読んでもらえるとオブジェクト指向の雰囲気をつかんでもらえるかと思います。
Kotlinもオブジェクト指向のプログラミング言語なので、クラスという考え方を理解する必要があります。このクラスが部品です。イメージをつかんでもらうために、人(Humanクラス)を考えてみます。
人(Human)は、名前、身長、体重というデータを持っています。ちょっと設定に無理がありますが、食べると体重が1kg増えて、走ると1kg減るという特徴もあります。これをプログラミングっぽく表現すると次の表になります。
人クラス | Human |
データ |
プロパティ(データメンバ) |
動作 |
メソッド(メンバ関数) |
これらのデータメンバやメソッド(メンバ関数)をまとめて『クラスメンバ』や『メンバ』とも呼びます。HumanクラスをKotlinで、表現すると次のようになります。
//Humanクラスの宣言
class Human(val name:String, val ht:Int, var wt:Int) {
//メソッドの宣言
fun eat(){
wt++ // wtを+1する(++をインクリメントと呼びます)
}
fun run(){
wt-- // wtを-1する(--をデクリメントと呼びます)
}
showData(){
print(${name}, ${ht}, ${wt}) //文字列連結
}
}
クラスをご紹介してきましたが、注意して欲しいのはクラスはまだ実体がないということです。「実体がないとはどういうこと?」と感じる方もおられるかもしれませんが、難しく考える必要はありません。
「人(Humanクラス)は、身長や体重、名前を保持することができる」のですが、クラスの状態では具体的なデータがまだありません。
・お名前は?
・具体的な身長や体重はいくつ?
つまりクラスは、まだ空っぽの容器の状態です。そのため、実体化(インスタンス化)して使う必要があります。実体化という言葉は難しそうですが、やることは簡単ですので安心してください。
インスタンス化
クラスの項で「クラスはまだ実体がない空っぽの状態」とお伝えしました。実体化(インスタンス化)するのは難しく考える必要はありません。空っぽのクラスに具体的なデータを持たせるだけです。太郎さん(taro)を生成するイメージを次の表にまとめます。
Human | taro |
プロパティ(クラス内のデータ) |
プロパティ |
メソッド(クラス内の関数) |
メソッド |
表の右ではtaroが具体的に浮かび上がります。これが実体化です。前回のクラスでご紹介したHumanクラスをインスタンス化(実体化)してみます。
//Humanクラスの宣言(まだ空っぽの状態)
class Human(val name:String, val ht:Int, var wt:Int) {
fun eat(){
wt++
}
fun run(){
wt--
}
fun showData(){
print("${name}, ${ht}, ${wt}")
}
}
val taro = Human("taro", 170, 60) // Humanクラスのtoroをインスタンス化
taro.eat() // wtを+1するメソッド
taro.showData() // taroのデータを表示するメソッド
ちなみにデータメンバは、次のように変更できます。
//varで宣言したデータメンバは変更可能
taro.wt = 70 // taroのwtを70に変更可能
//valで宣言したデータメンバは変更不可
taro.ht = 175 // エラー。Humanクラスのhtはvalで宣言しているので変更不可。
taro.name = "jiro" // エラー。nameもvalで宣言したため
クラスの状態はまだ空っぽ、インスタンス化(実体化)すると具体的なイメージが浮かび上がります。ちなみにインスタンス化したクラスのことを『オブジェクト』とも呼びます。
NULLとNULL安全
プログラミング言語で出てくる『NULL』という用語。NULLは「何もない状態」を表現します。「空っぽとはゼロという意味?」と感じた方もおられるかもしれませんが、『0』もInt型やString型などのデータ型を持つ値です。
NULLは変数という容器は用意されていますが、何も入っていない『空っぽ』の状態です。
プログラミングで空っぽを表すNULLは重要な考え方ではあるのですが、思わぬ不具合(バグ)の要因になることがあります。NULLによるエラーの発生を防止するために「NULLを許可しない」という思想のプログラミング言語を『NULL安全なプログラミング言語』と表現します。KotlinはNULL安全なプログラミング言語です。
var str:String = null //エラー ※NULLの代入は禁止されている
print(str)
Kotlinでどうしても変数にNULL(空っぽ)を代入する必要がある時には、安全呼び出し演算子『?』を使用します。次のコードで変数にNULLを代入してみます。
var str:String? = null // ? を付けると変数にNULLを代入することが許可される
print(str) // NULL
『?』を使用するとNULLも変数に代入することができました。
let演算子
Javaというプログラミング言語では、その変数にNULLが入っているとトラブルになる場合は「この変数にNULLが入っていないだろうか?」と確認する処理を行う手間がありました。
Kotlinでは、NULLを許可した変数(?付き)と組み合わせて『let演算子』を使用すると、変数の中身をチェックして、NULL以外なら実行、NULLなら実行しないという処理を簡単に行うことができます。
変数a?.let{ 処理A } // 変数aがNULLでなければ処理Aを実行する
実際にlet演算子を使用して変数がNULLかの判定を行い、NULLでなければ
Hello, ○○(変数名)
と表示してみます。
//let演算子での判定がNULLではない場合
val str:String? = "taro" // strは?を付けてNULLの代入を許可する
print(str?.let{"Hello, ${it}"}) // Hello, taro
次は変数にNULLが入っている場合の動作を確認してみます。
//let演算子での判定がNULLの場合
val str:String? = null // strは?を付けてNULLの代入を許可する
print(str?.let{"Hello, ${it}"}) // null
let演算子がNULLと判定した場合は、{}の処理が実行されないことを確認することができました。