関数
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
}
コーディング規約では、関数名を小文字で始め、アンダースコアなしのキャメルケースを使用することを推奨しています。
名前付き引数
簡潔なコードの場合、関数を呼び出す際にパラメータ名を含める必要はありません。ただし、パラメータ名を含めることでコードが読みやすくなります。これは名前付き引数の使用と呼ばれます。パラメータ名を含める場合、パラメータを任意の順序で記述できます。
以下の例では、文字列テンプレート (
) を使用して、パラメータ値にアクセスし、それらを
String
型に変換し、結合して文字列として出力しています。
fun printMessageWithPrefix(message: String, prefix: String) {
println("[$prefix] $message")
}
fun main() {
// Uses named arguments with swapped parameter order
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
デフォルト引数値
関数のパラメータにデフォルト値を定義できます。デフォルト値を持つパラメータは、関数を呼び出す際に省略できます。デフォルト値を宣言するには、型の後に代入演算子 =
を使用します。
fun printMessageWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message")
}
fun main() {
// Function called with both parameters
printMessageWithPrefix("Hello", "Log")
// [Log] Hello
// Function called only with message parameter
printMessageWithPrefix("Hello")
// [Info] Hello
printMessageWithPrefix(prefix = "Log", message = "Hello")
// [Log] Hello
}
デフォルト値を持つ特定のパラメータを、すべて省略するのではなく、スキップすることができます。ただし、最初のスキップしたパラメータの後ろにあるすべてのパラメータは、名前を指定する必要があります。
戻り値のない関数
関数が有用な値を何も返さない場合、その戻り値の型はUnit
になります。Unit
は、唯一の値であるUnit
を持つ型です。関数の本体でUnit
が明示的に返されることを宣言する必要はありません。これは、return
キーワードを使用したり、戻り値の型を宣言したりする必要がないことを意味します。
fun printMessage(message: String) {
println(message)
// `return Unit` or `return` is optional
}
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
}
ただし、他の開発者がコードを素早く理解できるようにしたい場合は、代入演算子 =
を使用している場合でも、戻り値の型を明示的に定義することをお勧めします。
関数本体を宣言するために
{}
波括弧を使用する場合、Unit
型でない限り、戻り値の型を宣言する必要があります。
関数での早期リターン
関数内のコードが特定の点を超えて処理されるのを停止するには、return
キーワードを使用します。この例では、条件式がtrue
と評価された場合に、if
を使用して関数から早期にリターンしています。
// A list of registered usernames
val registeredUsernames = mutableListOf("john_doe", "jane_smith")
// A list of registered emails
val registeredEmails = mutableListOf("[email protected]", "[email protected]")
fun registerUser(username: String, email: String): String {
// Early return if the username is already taken
if (username in registeredUsernames) {
return "Username already taken. Please choose a different username."
}
// Early return if the email is already registered
if (email in registeredEmails) {
return "Email already registered. Please use a different email."
}
// Proceed with the registration if the username and email are not taken
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()
関数は、ラムダ式を述語として受け入れ、リストの各要素に適用します。この関数は、述語が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倍した値を返します。
関数型
関数からラムダ式を返す前に、まず関数型を理解する必要があります。
すでに基本型については学びましたが、関数自体も型を持っています。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
値を返すラムダ式を返すためです。
この例では、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() {
// The initial value is zero.
// The operation sums the initial value with every item in the list cumulatively.
println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6
// Alternatively, in the form of a trailing lambda
println(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6
}
ラムダ式の詳細については、ラムダ式と匿名関数を参照してください。
ツアーの次のステップでは、Kotlinのクラスについて学びます。
ラムダ式の演習
演習 1
Webサービスがサポートするアクションのリスト、すべてのリクエストに共通のプレフィックス、および特定の資源の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")
}
}