関数
Kotlinでは、fun
キーワードを使用して独自の関数を宣言できます。
fun hello() {
return println("Hello, world!")
}
fun main() {
hello()
// Hello, world!
}
Kotlinでは:
- 関数パラメータは丸括弧
()
内に記述します。 - 各パラメータには型が必要であり、複数のパラメータはコンマ
,
で区切る必要があります。 - 戻り値の型は、関数の丸括弧
()
の後にコロン:
で区切って記述します。 - 関数の本体は中括弧
{}
内に記述します。 return
キーワードは、関数を終了するか、関数から何かを返すために使用されます。
次の例では:
x
とy
は関数パラメータです。x
とy
はInt
型です。- 関数の戻り値の型は
Int
です。 - この関数は呼び出されると
x
とy
の合計を返します。
fun sum(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
// 3
}
NOTE
コーディング規約では、関数名を小文字で始め、アンダースコアなしのキャメルケースを使用することを推奨しています。
名前付き引数
コードを簡潔にするために、関数を呼び出す際にパラメータ名を含める必要はありません。ただし、パラメータ名を含めると、 コードが読みやすくなります。これは名前付き引数の使用と呼ばれます。パラメータ名を含める場合、 パラメータを任意の順序で記述できます。
fun printMessageWithPrefix(message: String, prefix: String) {
println("[$prefix] $message")
}
fun main() {
// パラメータの順序を入れ替えて名前付き引数を使用
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
デフォルト引数値
関数パラメータにはデフォルト値を定義できます。デフォルト値を持つパラメータは、 関数を呼び出すときに省略できます。デフォルト値を宣言するには、型の後に代入演算子=
を使用します。
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
fun main() {
// 両方のパラメータを指定して関数を呼び出す
printMessageWithPrefix("Hello", "Log")
// [Log] Hello
// messageパラメータのみを指定して関数を呼び出す
printMessageWithPrefix("Hello")
// [Info] Hello
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
NOTE
すべてのデフォルト値を省略するのではなく、特定のパラメータをスキップできます。ただし、
最初にスキップしたパラメータの後は、後続のすべてのパラメータに名前を付ける必要があります。
戻り値のない関数
関数が有用な値を返さない場合、その戻り値の型はUnit
です。Unit
は唯一の値をUnit
として持つ型です。 関数の本体でUnit
が明示的に返されることを宣言する必要はありません。これは、 return
キーワードを使用したり、戻り値の型を宣言したりする必要がないことを意味します。
fun printMessage(message: String) {
println(message)
// `return Unit` または `return` はオプションです
}
fun main() {
printMessage("Hello")
// Hello
}
単一式関数
コードをより簡潔にするために、単一式関数を使用できます。たとえば、sum()
関数は短縮できます。
fun sum(x: Int, y: Int): Int {
return x + y
}
fun main() {
println(sum(1, 2))
// 3
}
中括弧 {}
を削除し、代入演算子 =
を使用して関数本体を宣言できます。 代入演算子 =
を使用する場合、Kotlinは型推論を使用するため、戻り値の型も省略できます。 すると、sum()
関数は1行になります。
fun sum(x: Int, y: Int) = x + y
fun main() {
println(sum(1, 2))
// 3
}
ただし、他の開発者にもコードがすぐに理解できるようにしたい場合は、代入演算子 =
を使用する場合でも、 戻り値の型を明示的に定義することをお勧めします。
NOTE
{}
中括弧を使用して関数本体を宣言する場合、それがUnit
型でない限り、戻り値の型を宣言する必要があります。
関数における早期リターン
関数内のコードが特定のポイントを超えて処理されないようにするには、return
キーワードを使用します。 次の例では、条件式がtrueと判断された場合に、if
を使用して関数から早期にリターンしています。
// 登録済みユーザー名のリスト
val registeredUsernames = mutableListOf("john_doe", "jane_smith")
// 登録済みメールアドレスのリスト
val registeredEmails = mutableListOf("[email protected]", "[email protected]")
fun registerUser(username: String, email: String): String {
// ユーザー名が既に取得されている場合は早期リターン
if (username in registeredUsernames) {
return "Username already taken. Please choose a different username."
}
// メールアドレスが既に登録されている場合は早期リターン
if (email in registeredEmails) {
return "Email already registered. Please use a different email."
}
// ユーザー名とメールアドレスが取得されていない場合は登録を続行
registeredUsernames.add(username)
registeredEmails.add(email)
return "User registered successfully: $username"
}
fun main() {
println(registerUser("john_doe", "[email protected]"))
// Username already taken. Please choose a different username.
println(registerUser("new_user", "[email protected]"))
// User registered successfully: new_user
}
関数プラクティス
演習1
整数の形式で円の半径をパラメータとして受け取り、その円の面積を出力するcircleArea
という関数を記述してください。
|---|---|
import kotlin.math.PI
// ここにコードを記述してください
fun main() {
println(circleArea(2))
}
|---|---|
import kotlin.math.PI
fun circleArea(radius: Int): Double {
return PI * radius * radius
}
fun main() {
println(circleArea(2)) // 12.566370614359172
}
演習2
前の演習のcircleArea
関数を単一式関数として書き直してください。
|---|---|
import kotlin.math.PI
// ここにコードを記述してください
fun main() {
println(circleArea(2))
}
|---|---|
import kotlin.math.PI
fun circleArea(radius: Int): Double = PI * radius * radius
fun main() {
println(circleArea(2)) // 12.566370614359172
}
演習3
時、分、秒で与えられた時間間隔を秒に変換する関数があります。ほとんどの場合、 1つまたは2つの関数パラメータのみを渡す必要があり、残りは0に等しくなります。 コードを読みやすくするために、デフォルト引数値と名前付き引数を使用して、 関数とそれを呼び出すコードを改善してください。
|---|---|
fun intervalInSeconds(hours: Int, minutes: Int, seconds: Int) =
((hours * 60) + minutes) * 60 + seconds
fun main() {
println(intervalInSeconds(1, 20, 15))
println(intervalInSeconds(0, 1, 25))
println(intervalInSeconds(2, 0, 0))
println(intervalInSeconds(0, 10, 0))
println(intervalInSeconds(1, 0, 1))
}
|---|---|
fun intervalInSeconds(hours: Int = 0, minutes: Int = 0, seconds: Int = 0) =
((hours * 60) + minutes) * 60 + seconds
fun main() {
println(intervalInSeconds(1, 20, 15))
println(intervalInSeconds(minutes = 1, seconds = 25))
println(intervalInSeconds(hours = 2))
println(intervalInSeconds(minutes = 10))
println(intervalInSeconds(hours = 1, seconds = 1))
}
ラムダ式
Kotlinでは、ラムダ式を使用することで、さらに簡潔な関数コードを記述できます。
たとえば、次のuppercaseString()
関数は:
fun uppercaseString(text: String): String {
return text.uppercase()
}
fun main() {
println(uppercaseString("hello"))
// HELLO
}
ラムダ式として記述することもできます:
fun main() {
val upperCaseString = { text: String -> text.uppercase() }
println(upperCaseString("hello"))
// HELLO
}
ラムダ式は一見すると理解しにくいかもしれませんが、分解して見てみましょう。ラムダ式は中括弧 {}
内に記述されます。
ラムダ式内には、次のように記述します。
- パラメータの後に
->
を記述します。 ->
の後に関数本体を記述します。
上記の例では:
text
は関数パラメータです。text
はString
型です。- 関数は
text
で呼び出された.uppercase()
関数の結果を返します。 - ラムダ式全体が代入演算子
=
を使用してupperCaseString
変数に割り当てられます。 - ラムダ式は、
upperCaseString
変数を関数のように使用し、文字列"hello"
をパラメータとして渡すことで呼び出されます。 println()
関数が結果を出力します。
NOTE
パラメータなしでラムダを宣言する場合、->
を使用する必要はありません。例:
{ println("Log message") }
ラムダ式はさまざまな方法で使用できます。次のことができます。
別の関数に渡す
ラムダ式を関数に渡すのが役立つ良い例は、コレクションで.filter()
関数を使用することです。
fun main() {
val numbers = listOf(1, -2, 3, -4, 5, -6)
val positives = numbers.filter ({ x -> x > 0 })
val isNegative = { x: Int -> x < 0 }
val negatives = numbers.filter(isNegative)
println(positives)
// [1, 3, 5]
println(negatives)
// [-2, -4, -6]
}
.filter()
関数は、述語としてラムダ式を受け入れます。
{ x -> x > 0 }
はリストの各要素を受け取り、正の要素のみを返します。{ x -> x < 0 }
はリストの各要素を受け取り、負の要素のみを返します。
この例は、ラムダ式を関数に渡す2つの方法を示しています。
- 正の数の場合、この例ではラムダ式を
.filter()
関数に直接追加しています。 - 負の数の場合、この例ではラムダ式を
isNegative
変数に割り当てています。次に、isNegative
変数が.filter()
関数の関数パラメータとして使用されます。この場合、 ラムダ式で関数パラメータ(x
)の型を指定する必要があります。
NOTE
ラムダ式が唯一の関数パラメータである場合、丸括弧 ()
を省略できます。
val positives = numbers.filter { x -> x > 0 }
これは末尾ラムダの例であり、この章の最後で詳しく説明します。
もう1つの良い例は、.map()
関数を使用して コレクション内の項目を変換することです。
fun main() {
val numbers = listOf(1, -2, 3, -4, 5, -6)
val doubled = numbers.map { x -> x * 2 }
val isTripled = { x: Int -> x * 3 }
val tripled = numbers.map(isTripled)
println(doubled)
// [2, -4, 6, -8, 10, -12]
println(tripled)
// [3, -6, 9, -12, 15, -18]
}
.map()
関数は、変換関数としてラムダ式を受け入れます。
{ x -> x * 2 }
はリストの各要素を受け取り、その要素を2倍した値を返します。{ x -> x * 3 }
はリストの各要素を受け取り、その要素を3倍した値を返します。
関数型
関数からラムダ式を返す前に、まず関数型を理解する必要があります。
基本型についてはすでに学習しましたが、関数自体も型を持ちます。Kotlinの型推論は、 パラメータの型から関数の型を推論できます。ただし、関数型を明示的に指定する必要がある場合があります。 コンパイラは関数型を必要とし、その関数に何が許可され、何が許可されないかを知るためです。
関数型の構文は次のとおりです。
- 各パラメータの型は丸括弧
()
内に記述され、コンマ,
で区切られます。 - 戻り値の型は
->
の後に記述されます。
例: (String) -> String
または(Int, Int) -> Int
。
upperCaseString()
の関数型が定義されている場合のラムダ式は次のようになります。
val upperCaseString: (String) -> String = { text -> text.uppercase() }
fun main() {
println(upperCaseString("hello"))
// HELLO
}
ラムダ式にパラメータがない場合、丸括弧 ()
は空になります。例: () -> Unit
NOTE
パラメータと戻り値の型は、ラムダ式内または関数型として宣言する必要があります。そうしないと、
コンパイラがラムダ式の型を認識できません。
たとえば、次のコードは動作しません。
val upperCaseString = { str -> str.uppercase() }
関数からの戻り値
ラムダ式は関数から返すことができます。コンパイラが返されるラムダ式の型を理解するには、 関数型を宣言する必要があります。
次の例では、toSeconds()
関数は関数型(Int) -> Int
を持っています。これは、常にInt
型のパラメータを受け取り、 Int
値を返すラムダ式を返すためです。
この例では、when
式を使用して、toSeconds()
が呼び出されたときにどのラムダ式が返されるかを決定しています。
fun toSeconds(time: String): (Int) -> Int = when (time) {
"hour" -> { value -> value * 60 * 60 }
"minute" -> { value -> value * 60 }
"second" -> { value -> value }
else -> { value -> value }
}
fun main() {
val timesInMinutes = listOf(2, 10, 15, 1)
val min2sec = toSeconds("minute")
val totalTimeInSeconds = timesInMinutes.map(min2sec).sum()
println("Total time is $totalTimeInSeconds secs")
// Total time is 1680 secs
}
個別に呼び出す
ラムダ式は、中括弧 {}
の後に丸括弧 ()
を追加し、その中にパラメータを含めることで、 単独で呼び出すことができます。
fun main() {
println({ text: String -> text.uppercase() }("hello"))
// HELLO
}
末尾ラムダ
すでに見たように、ラムダ式が唯一の関数パラメータである場合、関数の丸括弧 ()
を省略できます。 ラムダ式が関数の最後のパラメータとして渡される場合、その式は関数の丸括弧 ()
の外に記述できます。 どちらの場合も、この構文は末尾ラムダと呼ばれます。
たとえば、.fold()
関数は 初期値と演算を受け入れます。
fun main() {
// 初期値はゼロです。
// この演算は、初期値とリスト内のすべての項目を累積的に合計します。
println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6
// あるいは、末尾ラムダの形式で
println(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6
}
ラムダ式の詳細については、ラムダ式と匿名関数を参照してください。
ツアーの次のステップは、Kotlinのクラスについて学ぶことです。
ラムダ式プラクティス
演習1
ウェブサービスがサポートするアクションのリスト、すべてのリクエストの共通プレフィックス、および特定の リソースのIDがあります。ID: 5のリソースに対してアクションtitle
をリクエストするには、 https://example.com/book-info/5/title
というURLを作成する必要があります。 ラムダ式を使用して、アクションのリストからURLのリストを作成してください。
|---|---|
fun main() {
val actions = listOf("title", "year", "author")
val prefix = "https://example.com/book-info"
val id = 5
val urls = // ここにコードを記述してください
println(urls)
}
|---|---|
fun main() {
val actions = listOf("title", "year", "author")
val prefix = "https://example.com/book-info"
val id = 5
val urls = actions.map { action -> "$prefix/$id/$action" }
println(urls)
}
演習2
Int
値とアクション(型が() -> Unit
の関数)を受け取り、指定された回数だけそのアクションを繰り返す関数を記述してください。 次に、この関数を使用して「Hello」を5回出力してください。
|---|---|
fun repeatN(n: Int, action: () -> Unit) {
// ここにコードを記述してください
}
fun main() {
// ここにコードを記述してください
}
|---|---|
fun repeatN(n: Int, action: () -> Unit) {
for (i in 1..n) {
action()
}
}
fun main() {
repeatN(5) {
println("Hello")
}
}