関数
Hello world
基本の型 (Basic types)
コレクション (Collections)
制御フロー (Control flow)
関数 (Functions)
クラス (Classes)
Null安全 (Null safety)
Kotlinでは、funキーワードを使用して独自の関数を宣言できます。
fun hello() {
return println("Hello, world!")
}
fun main() {
hello()
// Hello, world!
}Kotlinにおいて:
- 関数のパラメータは丸括弧
()内に記述します。 - 各パラメータには型を指定する必要があり、複数のパラメータはカンマ
,で区切ります。 - 戻り値の型は、関数の丸括弧
()の後にコロン:で区切って記述します。 - 関数の本体は波括弧
{}内に記述します。 returnキーワードは、関数を終了したり、関数から値を返したりするために使用します。
関数が有用な値を返さない場合、戻り値の型と
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
}コーディング規約では、関数名は小文字で始め、アンダースコアを使用しないキャメルケース(camelCase)を使用することを推奨しています。
名前付き引数
コードを簡潔にするために、関数を呼び出す際にパラメータ名を含める必要はありません。しかし、パラメータ名を含めると、コードが読みやすくなります。これは名前付き引数 (Named arguments) と呼ばれます。パラメータ名を含める場合は、パラメータを任意の順序で記述できます。
以下の例では、パラメータの値にアクセスし、それらを
String型に変換して、印刷用に文字列として連結するために、文字列テンプレート ($) を使用しています。
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
}すべてを省略するのではなく、デフォルト値を持つ特定のパラメータのみをスキップすることもできます。ただし、最初にパラメータをスキップした後は、それ以降のすべてのパラメータに名前を付ける必要があります。
戻り値のない関数
関数が有用な値を返さない場合、その戻り値の型は Unit になります。Unit は Unit という1つの値しか持たない型です。関数本体で Unit が返されることを明示的に宣言する必要はありません。つまり、return キーワードを使用したり、戻り値の型を宣言したりする必要はありません。
fun printMessage(message: String) {
println(message)
// `return Unit` または `return` はオプション(省略可能)です
}
fun main() {
printMessage("Hello")
// Hello
}単一式関数
コードをより簡潔にするために、単一式関数 (Single-expression functions) を使用できます。例えば、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
}ただし、他の開発者がコードをすぐに理解できるようにしたい場合は、代入演算子 = を使用する場合でも、戻り値の型を明示的に定義するのが良い習慣です。
関数本体の宣言に
{}波括弧を使用する場合、戻り値の型がUnitでない限り、戻り値の型を宣言する必要があります。
関数内での早期リターン
関数のコードがある時点以降処理されないようにするには、return キーワードを使用します。この例では、条件式が真である場合に関数から早期リターン (Early return) するために 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 という名前の関数を作成してください。
TIP
この演習では、PI を介して の値にアクセスできるようにパッケージをインポートします。パッケージのインポートに関する詳細は、パッケージとインポートを参照してください。
ヒント
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))
}別の関数に渡す
ラムダ式を関数に渡すのが便利な代表例は、コレクションに対して .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() 関数は述語 (predicate) としてラムダ式を受け取り、それをリストの各要素に適用します。関数は、述語が true を返した要素のみを保持します。
{ x -> x > 0 }は、要素が正の場合にtrueを返します。{ x -> x < 0 }は、要素が負の場合にtrueを返します。
この例では、ラムダ式を関数に渡す2つの方法を示しています。
- 正の数の場合、
.filter()関数内に直接ラムダ式を追加しています。 - 負の数の場合、ラムダ式を変数
isNegativeに代入しています。その後、変数isNegativeが.filter()関数の引数として使用されています。この場合、ラムダ式内の関数パラメータ (x) の型を指定する必要があります。
ラムダ式が唯一の関数パラメータである場合、関数の丸括弧
()を省略できます:kotlinval 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を掛けた値を返します。
関数型
関数からラムダ式を返せるようになる前に、まず関数型 (Function types) を理解する必要があります。
基本の型についてはすでに学びましたが、関数自体も型を持っています。Kotlinの型推論は、パラメータの型から関数の型を推論できます。しかし、関数の型を明示的に指定する必要がある場合もあります。コンパイラは、その関数で何が許可され、何が許可されないかを知るために関数型を必要とします。
関数型の構文は以下の通りです:
- 各パラメータの型を丸括弧
()内に書き、カンマ,で区切ります。 - 戻り値の型を
->の後に書きます。
例:(String) -> String や (Int, Int) -> Int。
upperCaseString() の関数型を定義すると、ラムダ式は以下のようになります:
val upperCaseString: (String) -> String = { text -> text.uppercase() }
fun main() {
println(upperCaseString("hello"))
// HELLO
}ラムダ式にパラメータがない場合、丸括弧 () は空のままにします。例:() -> Unit
パラメータと戻り値の型は、ラムダ式内か関数型として宣言する必要があります。そうしないと、コンパイラはラムダ式の型を特定できません。
例えば、以下は動作しません:
val upperCaseString = { str -> str.uppercase() }
関数から返す
ラムダ式は関数から返すことができます。コンパイラが返されるラムダ式の型を理解できるように、関数型を宣言する必要があります。
以下の例では、toSeconds() 関数は常に Int 型のパラメータを受け取り Int 値を返すラムダ式を返すため、関数型は (Int) -> Int です。
この例では、toSeconds() が呼び出されたときにどのラムダ式を返すかを決定するために when 式を使用しています:
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
}後置ラムダ
すでに見たように、ラムダ式が唯一の関数パラメータである場合、関数の丸括弧 () を省略できます。ラムダ式が関数の最後のパラメータとして渡される場合、その式を関数の丸括弧 () の外に記述できます。どちらの場合も、この構文は後置ラムダ (Trailing lambda) と呼ばれます。
例えば、.fold() 関数は初期値と演算を受け取ります:
fun main() {
// 初期値は0。
// 演算は、初期値とリストの各アイテムを累積的に加算します。
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 をリクエストするには、次のURLを作成する必要があります:https://example.com/book-info/5/title。ラムダ式を使用して、アクションのリストから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")
}
}