1. 始める前に
以前の Codelab では、Hello, world!
を出力するシンプルなプログラムを作成しました。ここまでに作成したプログラムで、以下の 2 つの関数を見てきました。
main()
関数(すべての Kotlin プログラムに必要)。これは、プログラムのエントリ ポイント(開始点)です。println()
関数(テキストを出力するためにmain()
から呼び出した関数)。
この Codelab では、関数についてより深く学習します。
関数を使用すると、コードを main()
にすべて含めるのではなく、再利用可能な部分に分割できます。関数は Android アプリの重要な構成要素です。関数の定義と使用方法を学ぶことは、Android デベロッパーになるための大きな一歩となります。
前提条件
- Kotlin プログラミングの基礎知識(変数を含む)、および
println()
関数とmain()
関数の知識
学習内容
- 独自の関数を定義して呼び出す方法
- 関数から値(変数に格納できる)を返す方法
- 複数のパラメータを持つ関数を定義して呼び出す方法
- 名前付き引数を使用して関数を呼び出す方法
- 関数パラメータのデフォルト値を設定する方法
必要なもの
- Kotlin プレイグラウンドにアクセスできるウェブブラウザ
2. 関数を定義して呼び出す
関数について詳しく学習する前に、基本的な用語をおさらいしましょう。
- 関数を宣言(定義)するには、
fun
キーワードを使用し、タスクの実行に必要な手順を含むコードを中かっこの中に記述します。 - 関数を呼び出すと、その関数に含まれるすべてのコードが実行されます。
これまでは、すべてのコードを main()
関数内に記述していました。main()
関数は、実際にはコード内の任意の場所で呼び出されるわけではなく、Kotlin コンパイラによって開始点として使用されます。main()
関数は、println()
関数の呼び出しなどを実行する別のコードを含めることのみを目的とする関数です。
println()
は、Kotlin 言語で用意されている関数です。ただし、独自の関数を定義することもできます。これにより、コードを複数回呼び出す必要がある場合にコードを再利用できます。以下のプログラムで例を示します。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
main()
関数は、2 つの println()
ステートメント(Rover の誕生日を祝うメッセージと、Rover の年齢を述べるメッセージ)で構成されています。
Kotlin ではすべてのコードを main()
関数に含めることができますが、必ずしもそうする必要はありません。たとえば、新年のあいさつもプログラムに含める場合は、main 関数でその println()
の呼び出しも追加する必要があります。Rover に何度もお祝いを言いたい場合もあるでしょう。単にコードをコピーして貼り付けることも、誕生日祝い用の関数を個別に作成することもできます。ここでは後者で進めます。タスクごとに個別の関数を作成すると、以下のようなメリットがあります。
- コードが再利用可能になる: 何度も使用する必要があるコードをコピーして貼り付ける代わりに、必要なときに 1 つの関数を呼び出すだけで済みます。
- コードが読みやすくなる: 関数が特定のタスクを 1 つだけ実行するようにすれば、他の開発者やチームメイトだけでなく、未来の自分自身もコードが何をしているかを正確に把握するのに役立ちます。
関数を定義する構文を次の図に示します。
関数の定義は fun
キーワードで始まり、その後に関数の名前、開きかっこと閉じかっこのペア、開き中かっこと閉じ中かっこのペアが続きます。中かっこの中には、関数を呼び出したときに実行されるコードを記述します。
新しい関数を作成し、2 つの println()
ステートメントを main()
関数から移動させます。
- ブラウザで Kotlin プレイグラウンドを開き、その内容を以下のコードに置き換えます。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
main()
関数の後に、birthdayGreeting()
という名前の新しい関数を定義します。この関数は、main()
関数と同じ構文で宣言します。
fun main() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
fun birthdayGreeting() {
}
- 2 つの
println()
ステートメントをmain()
からbirthdayGreeting()
関数の中かっこ内に移動します。
fun main() {
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
main()
関数で、birthdayGreeting()
関数を呼び出します。完成したコードは以下のようになります。
fun main() {
birthdayGreeting()
}
fun birthdayGreeting() {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- コードを実行します。次の出力が表示されます。
Happy Birthday, Rover! You are now 5 years old!
3. 関数から値を返す
もっと複雑なアプリでは、関数はテキストを出力するだけではありません。
Kotlin 関数は、「戻り値」と呼ばれるデータも生成できます。戻り値は変数に格納されます。変数はコード内の別の場所で使用できます。
関数を定義するときは、関数で返す値のデータ型を指定できます。戻り値の型を指定するには、かっこの後にコロン(:
)を置き、スペースを 1 つ空けて、型の名前(Int
、String
など)を置きます。次に、戻り値の型と開き中かっこの間にスペースを 1 つ空けます。関数本体の中では、すべてのステートメントの後に return 文を使用して関数が返すべき値を指定します。return ステートメントは、return
キーワードと、その後に続く変数などの関数が出力として返す値で構成されます。
戻り値の型がある関数を宣言する構文は次のとおりです。
Unit
型
デフォルトでは、戻り値の型を指定しなかった場合、デフォルトの戻り値の型は Unit
になります。Unit
は、関数が値を返さないことを意味します。Unit
は、他の言語の void という戻り値の型と同等です(例: Java と C では void
、Swift では Void
/ 空のタプル ()
、Python では None
)。値を返さない関数は、暗黙的に Unit
を返します。Unit
を返すようにコードを変更すると、このことを確認できます。
birthdayGreeting()
の関数宣言で、閉じかっこの後にコロンを追加し、戻り値の型をUnit
として指定します。
fun main() {
birthdayGreeting()
}
fun birthdayGreeting(): Unit {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- コードを実行して、すべてが正常に機能していることを確認します。
Happy Birthday, Rover! You are now 5 years old!
Kotlin では、戻り値の型が Unit
の場合、指定は省略できます。何も返さない関数、または Unit
を返す関数の場合、return 文は必要ありません。
birthdayGreeting()
から String
を返す
関数がどのように値を返すかを確認するため、単に結果を出力するのではなく、文字列を返すように birthdayGreeting()
関数を変更します。
- 戻り値の型
Unit
をString
に置き換えます。
fun birthdayGreeting(): String {
println("Happy Birthday, Rover!")
println("You are now 5 years old!")
}
- コードを実行します。エラーが発生します。関数の戻り値の型(
String
など)を宣言する場合、その関数にはreturn
文が含まれていなければなりません。
A 'return' expression required in a function with a block body ('{...}')
- 関数から返すことができる文字列は 1 つのみであり、2 つ返すことはできません。
val
キーワードを使用して、println()
ステートメントをnameGreeting
とageGreeting,
の 2 つの変数に置き換えます。birthdayGreeting()
からprintln()
の呼び出しを削除したため、birthdayGreeting()
を呼び出しても何も出力されません。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
}
- 前の Codelab で学んだ文字列形式の構文を使用して、関数から 2 つのメッセージで構成される文字列を返す
return
文を追加します。
また、別々の行で 2 つのメッセージの書式を設定するには、\n
エスケープ文字を使用する必要があります。これは、前の Codelab で学んだ \"
エスケープ文字と非常に類似しています。\n
文字は改行に置き換えられ、2 つのメッセージはそれぞれ別の行に表示されます。
fun birthdayGreeting(): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
main()
では、birthdayGreeting()
によって値が返されるため、結果を文字列変数に格納できます。val
を使用してgreeting
変数を宣言し、birthdayGreeting()
の呼び出し結果を保存します。
fun main() {
val greeting = birthdayGreeting()
}
main()
で、println()
を呼び出してgreeting
文字列を出力します。main()
関数は以下のようになります。
fun main() {
val greeting = birthdayGreeting()
println(greeting)
}
- コードを実行し、結果が前と同じになることを確認します。値を返すと、結果を変数に格納できます。では、
println()
関数内でbirthdayGreeting()
関数を呼び出すとどうなるでしょうか。
Happy Birthday, Rover! You are now 5 years old!
- 変数を削除して、
birthdayGreeting()
関数の呼び出し結果をprintln()
関数に渡します。
fun main() {
println(birthdayGreeting())
}
- コードを実行し、出力を確認します。
birthdayGreeting()
を呼び出したときの戻り値は、そのままprintln()
に渡されます。
Happy Birthday, Rover! You are now 5 years old!
4. birthdayGreeting() 関数にパラメータを追加する
前に見たとおり、println()
関数を呼び出す際に、かっこ内に文字列を含めるか、関数に「値を渡す」ことができます。birthdayGreeting()
関数でも同じことができます。ただし、まず birthdayGreeting()
にパラメータを追加する必要があります。
パラメータは、関数内でアクセスするデータとして、関数に渡す変数の名前とデータ型を指定します。パラメータは、関数名の後のかっこ内で宣言します。
各パラメータは、コロンとスペースで区切られた変数名とデータ型で構成されます。パラメータが複数ある場合はカンマで区切ります。
現時点では、birthdayGreeting()
関数を使用できるのは、Rover にお祝いのメッセージを伝える場合に限られます。birthdayGreeting()
関数にパラメータを追加して、関数に渡した任意の名前の相手にお祝いのメッセージを伝えるようにします。
birthdayGreeting()
関数のかっこ内に、name: String
構文を使用してString
型のname
パラメータを追加します。
fun birthdayGreeting(name: String): String {
val nameGreeting = "Happy Birthday, Rover!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
前のステップで定義したパラメータは、val
キーワードで宣言された変数と同じように機能します。その値は、birthdayGreeting()
関数の任意の場所で使用できます。前の Codelab では、変数の値を文字列に挿入する方法を学びました。
nameGreeting
文字列内のRover
を、$
記号の後にname
パラメータを付けたものに置き換えます。
fun birthdayGreeting(name: String): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- コードを実行し、エラーを確認します。
name
パラメータを宣言したので、birthdayGreeting()
を呼び出すときにString
で渡す必要があります。パラメータを取る関数を呼び出すときは、関数に引数を渡します。引数は、関数に渡す値("Rover"
など)です。
No value passed for parameter 'name'
main()
のbirthdayGreeting()
呼び出しに"Rover"
を渡します。
fun main() {
println(birthdayGreeting("Rover"))
}
- コードを実行し、出力を確認します。
name
パラメータで指定した Rover という名前が出力されます。
Happy Birthday, Rover! You are now 5 years old!
birthdayGreeting()
はパラメータを取るので、Rover 以外の名前を指定して呼び出すこともできます。println()
の呼び出し内にbirthdayGreeting()
の呼び出しをもう一つ追加して、引数"Rex"
を渡します。
println(birthdayGreeting("Rover"))
println(birthdayGreeting("Rex"))
- コードを再度実行し、
birthdayGreeting()
に渡した引数に基づいて異なる結果が出力されることを確認します。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 5 years old!
5. 複数のパラメータを持つ関数
前のページでは、名前に応じてメッセージを変更するパラメータを追加しました。しかし、1 つの関数に複数のパラメータを(データ型が異なるパラメータであっても)定義することもできます。このセクションでは、メッセージを修正して、犬の年齢に応じて変更されるようにします。
パラメータ定義はカンマで区切ります。同様に、複数のパラメータを指定して関数を呼び出す場合、渡される引数もカンマで区切ります。実際に見てみましょう。
birthdayGreeting()
関数のname
パラメータの後に、Int
型のage
パラメータを追加します。新しい関数宣言では、name
とage
という 2 つのパラメータをカンマで区切って指定する必要があります。
fun birthdayGreeting(name: String, age: Int): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now 5 years old!"
return "$nameGreeting\n$ageGreeting"
}
- 新しいメッセージの文字列では、
age
パラメータを使用する必要があります。ageGreeting
文字列のage
パラメータの値を使用するように、birthdayGreeting()
関数を更新します。
fun birthdayGreeting(name: String, age: Int): String {
val nameGreeting = "Happy Birthday, $name!"
val ageGreeting = "You are now $age years old!"
return "$nameGreeting\n$ageGreeting"
}
- 関数を実行し、出力に含まれるエラーを確認します。
No value passed for parameter 'age' No value passed for parameter 'age'
- 犬ごとに異なる年齢を渡すように、
main()
のbirthdayGreeting()
関数に対する 2 つの呼び出しを変更します。Rover の年齢を5
、Rex の年齢を2
として渡します。
fun main() {
println(birthdayGreeting("Rover", 5))
println(birthdayGreeting("Rex", 2))
}
- コードを実行します。両方のパラメータの値を渡したので、関数を呼び出すと、それぞれの犬の名前と年齢が出力に反映されます。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
関数のシグネチャ
ここまで、関数名、入力(パラメータ)、出力を定義する方法について見てきました。関数名と入力(パラメータ)の組み合わせを関数のシグネチャと総称します。関数のシグネチャはすべて戻り値の型の前に構成されており、次のコード スニペットに表示されています。
fun birthdayGreeting(name: String, age: Int)
カンマで区切られたパラメータは、パラメータ リストと呼ばれることもあります。
他のデベロッパーが作成したコードのドキュメントで、こうした用語が使用されている場合が多く見られます。関数のシグネチャから関数の名前と、渡すことができるデータ型を判別できます。
関数の定義にまつわる多数の新しい構文について学びました。関数の構文のまとめについては、以下の図をご覧ください。
6. 名前付き引数
前の例では、関数を呼び出すときにパラメータ名である name
や age
を指定する必要はありませんでしたが、必要に応じて指定できます。たとえば、多数のパラメータを持つ関数を呼び出すことや、異なる順序で引数を渡す(例: name
パラメータの前に age
パラメータを置く)ことができます。関数を呼び出すときにパラメータ名を指定する場合、その引数を名前付き引数と呼びます。birthdayGreeting()
関数で名前付き引数を使用してみましょう。
- 以下のコード スニペットに示すとおり、名前付き引数を使用するように Rex の呼び出しを変更します。そのためには、パラメータ名、等号、値を続けて指定します(例:
name = "Rex"
)。
println(birthdayGreeting(name = "Rex", age = 2))
- コードを実行し、出力が変わらないことを確認します。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
- 名前付き引数の順序を変更します。たとえば、名前付き引数
name
の前に名前付き引数age
を置きます。
println(birthdayGreeting(age = 2, name = "Rex"))
- コードを実行し、出力が変わらないことを確認します。引数の順序を変更した場合でも、同じパラメータに同じ値が渡されます。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
7. デフォルトの引数
関数のパラメータには、デフォルトの引数を指定することもできます。Rover はお気に入りの犬かもしれませんし、ほとんどの場合で関数が特定の引数で呼び出されることが想定されるかもしれません。関数を呼び出すとき、デフォルトが存在する引数は省略できます。省略すると、デフォルトが使用されます。
デフォルトの引数を追加するには、パラメータのデータ型の後に代入演算子(=
)を追加し、特定の値を設定します。デフォルトの引数を使用するように、コードを変更します。
birthdayGreeting()
関数で、name
パラメータにデフォルト値の"Rover"
を設定します。
fun birthdayGreeting(name: String = "Rover", age: Int): String {
return "Happy Birthday, $name! You are now $age years old!"
}
main()
内の Rover に対するbirthdayGreeting()
の最初の呼び出しで、名前付き引数age
を5
に設定します。age
パラメータはname
の後で定義されるので、名前付き引数age
を使用する必要があります。名前付き引数がない場合、Kotlin では、引数の順序はパラメータが定義されている順序と同じであると見なされます。名前付き引数を使用することで、Kotlin がage
パラメータに対してInt
を想定することが保証されます。
println(birthdayGreeting(age = 5))
println(birthdayGreeting("Rex", 2))
- コードを実行します。
birthdayGreeting()
関数の最初の呼び出しでは、名前を指定しなかったため、名前として「Rover」が出力されます。birthdayGreeting()
の 2 回目の呼び出しでは、name
に渡したRex
という値が引き続き使用されます。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rex! You are now 2 years old!
birthdayGreeting()
関数の 2 回目の呼び出しから、名前を削除します。この場合も、name
が省略されているため、年齢に対して名前付き引数を使用する必要があります。
println(birthdayGreeting(age = 5))
println(birthdayGreeting(age = 2))
- コードを実行します。名前付き引数を渡していないため、
birthdayGreeting()
の両方の呼び出しで、名前として「Rover」が出力されることを確認します。
Happy Birthday, Rover! You are now 5 years old! Happy Birthday, Rover! You are now 2 years old!
8. おわりに
お疲れさまでした。ここでは Kotlin で関数を定義して呼び出す方法について学習しました。
まとめ
- 関数は
fun
キーワードで定義され、再利用可能なコードを含みます。 - 関数を使用することで大規模なプログラムの保守が容易になり、不要なコードの重複を回避できます。
- 関数で変数に格納できる値を返し、後で使用できます。
- 関数はパラメータを取ることができます。パラメータとは、関数本体の内部で利用可能な変数です。
- 引数は、関数を呼び出すときに渡す値です。
- 引数には、関数を呼び出すときに名前を付けることができます。名前付き引数を使用する場合は、出力に影響を与えることなく引数の順序を変更できます。
- デフォルトの引数を指定することで、関数を呼び出す際に引数を省略できます。