Kotlinのvaragr

Kotlinのvaragr

kotlinlang.org

ドキュメントによると「Variable number of arguments」の略らしい。
function(fun)に複数の変数を渡すことが可能と記載されています。

Javaなら下記のような感じです。 testMethodの (String... strs) 部分です。

    public static void main(String args[]) {
        testMethod("a1", "b1", "c1");
    }

    private static void testMethod(String... strs) {
        Arrays.stream(strs).forEach(System.out::println); // a1 b1 c1
    }
上記をKotlinで記述すると
fun main(args: Array<String>) {
    testMethod("a1", "b1", "c1")
}

private fun testMethod(vararg strs: String) {
    Arrays.stream(strs).forEach { x: String? -> println(x) } // a1 b1 c1
}

同じ型(String)の引数をvarargを使って、渡すことができます。

KotlinのlistOfはvarargでできている
fun main(args: Array<String>) {
    val test :List<String> = listOf<String>("a1", "b1", "c1")
    println(test) // [a1, b1, c1]
}

KotlinをlistOfを使うことがよくありますが、これもlistOfの引数はvarargなんですよね。 例えば、listOfに似たFunction(fun)を作ってみます。

下記のように、listOf01のfunctionを作成してみます。vararg elementsはJavaジェネリクスですね。
Functionの引数にジェネリクスを定義したらfunの後に <T> ジェネリクスの型を定義します。
(この辺りは、Javaと同じでこういうものだと理解した方が良いでしょう)

戻り値はListを返します。Listなので、List内の方はString、Int、data classなど型指定しません。 実装内部は、mutableListOfを作成し、Listにaddしていくだけです。

fun main(args: Array<String>) {
    val test :List<String> = listOf01<String>("a1", "b1", "c1")
    println(test) // [a1, b1, c1]
}

fun <T> listOf01(vararg elements: T) :List<T> {
    val list = mutableListOf<T>()
    elements.forEach {
        list.add(it)
    }
    return list
}
スプレッド演算子 (*)

次のようなCarというdata calassのListをvaragrを使って、print01ファンクションで標準出力するにはどのようにすればよいでしょうか?

data class Car(val maker: String, val model: String, val year: Int)

fun main(args: Array<String>) {
    val list = listOf<Car>(
            Car("Toyota", "COROLLA", 1966),
            Car("Toyota", "Alphard", 2002),
            Car("Toyota", "PRIUS", 1997)
    )
    // print01ファンクションにlistを渡して標準出力する
    // print01(list)  ではコンパイルエラーとなります
}

fun print01(vararg list : Car) {
    println(list)
}

上記のような場合は、スプレッド演算子 (*) を使います。 この辺りも慣れでしょう。

data class Car(val maker: String, val model: String, val year: Int)

fun main(args: Array<String>) {
    val list = listOf<Car>(
            Car("Toyota", "COROLLA", 1966),
            Car("Toyota", "Alphard", 2002),
            Car("Toyota", "PRIUS", 1997)
    )

    print01(*list.toTypedArray())
}
fun print01(vararg list : Car) {
    println(list) // [Car(maker=Toyota, model=COROLLA, year=1966), Car(maker=Toyota, model=Alphard, year=2002), Car(maker=Toyota, model=PRIUS, year=1997)]
}

上記のようにlistをまず、toTypedArrayを使ってArray型に変換します。
次に、スプレッド演算子 (*) を使って可変長引数に変換するようにします。
(Array型を配列にする)

スプレッド演算子 (*) は次のように型の配列を渡しているのと同じようようになります。

data class Car(val maker: String, val model: String, val year: Int)

fun main(args: Array<String>) {
    print01(
            Car("Toyota", "COROLLA", 1966),
            Car("Toyota", "Alphard", 2002),
            Car("Toyota", "PRIUS", 1997)
    )
}
fun print01(vararg list : Car) {
    println(list) // [Car(maker=Toyota, model=COROLLA, year=1966), Car(maker=Toyota, model=Alphard, year=2002), Car(maker=Toyota, model=PRIUS, year=1997)]
}

Kotlinのvaragr

Kotlinのvaragr

kotlinlang.org

ドキュメントによると「Variable number of arguments」の略らしい。
function(fun)に複数の変数を渡すことが可能と記載されています。

Javaなら下記のような感じです。 testMethodの (String... strs) 部分です。

    public static void main(String args[]) {
        testMethod("a1", "b1", "c1");
    }

    private static void testMethod(String... strs) {
        Arrays.stream(strs).forEach(System.out::println); // a1 b1 c1
    }

Kotlinで階乗計算(再起呼び出し)

引数をnを渡す
nが5の場合は、5 * 4 * 3 * 2 * 1 の処理結果を返す
nが3の場合は、3 * 2 * 1の処理結果を返す
1関数で再起処理で結果を返す(ループなどで var sum:Int = 0などで結果値を変数で保持しない)

fun factorial(n: Int): Int {
    return if (n <= 0) 1 else n * factorial(n - 1)
}

// 呼び出し
println(factorial(3)) // 3 * 2 * 1 = 6
println(factorial(5)) // 5 * 4 * 3 * 2 * 1 = 120
println(factorial(10)) // 10 * 9 .... = 3628800
BigIntegerで階乗計算
fun factorial(n: BigInteger): BigInteger {
    return if (n <= BigInteger.ONE) BigInteger.ONE else n * factorial(n - BigInteger.ONE)
}

// 呼び出し
println(factorial(BigInteger.valueOf(100)))
// 結果
// 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Kotlinメソッド(関数)

メソッドはfunで定義する

fun hoge1(){
    println("hoge1")
}

// 呼び出し
hoge1() // hoge1

引数ありのメソッド

fun hoge1(x: Int, y: Int){
    println("x = $x. y = $y ")
}

// 呼び出し
hoge1(5, 19) // x = 5. y = 19

戻り値、引数ありのメソッド

fun hoge1(x: Int, y: Int): Int {
    return x * y
}

// 呼び出し
println(hoge1(5, 19)) // 95

戻り値が2つの場合

Pairクラスを使う

fun hoge1(x: Int, y: Int): Pair<Int, String> {
    return (x * y) to "$x and $y"
}

// 呼び出し
val ret: Pair<Int, String> = hoge1(5, 19)
println(ret.first) // 95
println(ret.second) // 5 and 19

戻り値が3つの場合

Tripleクラスを使う

fun hoge1(x: Int, y: Int): Triple<Int, String, String> {
    return Triple(x * y , "$x and $y", "$x * $y")
}

// 呼び出し
val ret: Triple<Int, String, String> = hoge1(5, 19)
println(ret.first) // 95
println(ret.second) // 5 and 19
println(ret.third) // 5 * 19

PairやTripleはJavaApache commonsライブラリのTupleパッケージに似ている
commons.apache.org

Kotlinの場合は、下記のようにメソッド呼び出し時に型宣言を省略できる
val ret = hoge1(5, 19)

戻り値が4つの場合(Pairを組み合わせた場合)

Pairを組み合わせる

fun hoge1(x: Int, y: Int): Pair<Pair<Pair<Int, String>, Int>, String> {
    return (x * y) to "$x * $y" to (x + y) to "$x + $y"
}

// 呼び出し
val ret: Pair<Pair<Pair<Int, String>, Int>, String> = hoge1(5, 19)
println(ret.first.first.first) // 95
println(ret.first.first.second) // 5 and 19
println(ret.first.second) // 24
println(ret.second) // 5 + 19

戻り値が4つの場合(戻り値用のData Classを作成した場合)

Pairを組み合わせた場合に可読性が悪くなるので、戻り値クラスを作成する

data class Ret(val a: Int, val b: String, val c: Int, val d: String)

fun hoge1(x: Int, y: Int): Ret {
    return Ret((x * y), "$x * $y",(x + y) , "$x + $y")
}

// 呼び出し
val ret = hoge1(5, 19)
println(ret.a) // 95
println(ret.b) // 5 * 19
println(ret.c) // 24
println(ret.d) // 5 + 19

メソッド引数にNULLがある場合

引数の型の後に「?」を付ける

fun hoge1(x: Int?): Int {
    return x?.let {
        x * 10
    } ?: 0
}

// 呼び出し
println(hoge1(null)) // 0
println(hoge1(5)) // 50

関数型(Lambda)

次の例では、x、yのInt型を引数とし、戻り値にString型を定義している
val aCodeBlock: (x: Int, y: Int) -> String

実処理をブロック内に宣言する
ブロック内のx: Int, y: Int ->は宣言の引数である
実処理はif文であり、return は必要なし

(ほぼScalaと同じ書き方)

val aCodeBlock: (x: Int, y: Int) -> String = {
        if (x > y) "$x is greater than $y" else "$x is less than $y"
}

呼び出し方法は次のようになる

println(aCodeBlock(5, 3)) // 5 is greater than 3
戻り値が2つ以上

Pairなどを使用する

// 引数はx , yのInt型、戻り値はPairのInt型とString型
val bCodeBlock: (x: Int, y: Int) -> Pair<Int, String> = { x: Int, y: Int ->
    val ret1 = x * y
    val ret2: String = if (x > y) "$x is greater than $y" else "$x is less than $y"
    ret1 to ret2
}

// 呼び出し
val ret = bCodeBlock(5, 3)
println(ret.first) // 15
println(ret.second) // 5 is greater than 3
引数が1つの時はitを使用可能
val cCodeBlock:(x: Int) -> Int = {
    it * it
}

println(cCodeBlock(5)) // 25

上記は、次のようにコーディングしても同じ

val cCodeBlock: (x: Int) -> Int = { x: Int ->
    x * x
}

Kotlinの型と変数

Kotlinの型と変数

  1. Javaのintやbooleanといったプリミティブはない
  2. Javaのような文末にセミコロンは必要ない
  3. 変数の宣言はvarとvalが存在する。varはmutable(値の変更可能)、valはimmutable(値の変更不可)となる
data class Person(val age :Int,  val birth: java.time.LocalDate)

 val aLong : Long = 99999999L
 val bInt : Int = -4233
 val cShort : Short = 100
 val dByte : Byte = 1
 val eFloat : Float = 1.1F
 val fDouble : Double = 3.14144
 val gString : String = "Hello! World"
 val hChar : Char = 'C'
 val iBoolean : Boolean = true
 val jBigDecimal: java.math.BigDecimal = java.math.BigDecimal(3.2)
 val kPerson: Person = Person(15, java.time.LocalDate.parse("2005-12-04"))

 println("aLong=$aLong bInt=$bInt cShort=$cShort dByte=$dByte" +
         "eFloat=$eFloat fDouble=$fDouble gString=$gString hChar=$hChar " +
         "iBoolean=$iBoolean jBigDecimal=$jBigDecimal kPerson=$kPerson")

//  aLong=99999999 bInt=-4233 cShort=100 dByte=1eFloat=1.1
//  fDouble=3.14144 gString=Hello! World hChar=C iBoolean=true 
//  jBigDecimal=3.20000000000000017763568394002504646778106689453125 kPerson=Person(age=15, birth=2005-12-04)

Kotlinのvarとval

valは一度、定義すると代入不可

val x: Int = 42
// コンパイルできない
x = aa

varは値の変更可能

var c: String = "Hello!"
println(c) // "Hello!"
c = "Hi!"
println(c) // Hi!

varであっても、次のような宣言はnullが代入できない

var c: String = "Hello!"
c = null

varでnullを代入場合は、Nullable型で宣言する 型宣言時に?を付与する
?(セーフガード)がない場合は、nullを代入できない

var c: String? = "Hello!"
c = null

Kotlinのlet関数

  1. Lambda方式で記述する
  2. itは、Lambda内に渡される値。下記の例では、"Hi! Guys."にあたる
  3. 最後に定義した値が返却される
val sampleA: String? = "Hi! Guys."
val sampleB = sampleA.let {
    "$it How are you?"
}
 println(sampleB) // Hi! Guys. Hello! World"

次の例では、「sampleA?.let」と記述している
この場合、sampleAがNULLでない場合にのみlet内が処理される
つまり、「?.let」は変数の値がnullの場合は処理しない

val sampleA: Int? = null
println(sampleA?.let { it * 5 }) // null

上記をJavaで記述すると下記のようになる

Integer sampleA = null;
if (sampleA != null) {
    sampleA = sampleA * 5;
}
System.out.println(sampleA);