5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

kotlinで詰まったポイントと解決策

Last updated at Posted at 2019-04-25

1. 引数の型を List とした場合の型判定

Error
fun doSomething(arg: List<Any>): List<Int> = 
  when(arg) {
    is List<Int> -> ...
    is List<String> -> ...
    else -> ...
  }

これだと、3-4行目のisによるキャストで Cannot check for instance of erased typeというエラーを生じた。

List<Any>型は 、

"List型であることはわかるけど、中身の型の情報は持っていない"

つまり中身の型は消されている型(erased type)となるので、引数argの型をこのように判定することはできない。

一方で、次のような型判定は可能である。

OK
fun doSomething(arg: List<Any>): List<Int> = 
  when(arg[0]) {
    is Int -> ...
    is String -> ...
    else -> ...
  }

argの"要素"はAny型なので、これらはisを用いて判定することができる。

2. アスタリスク(*)とAnyの違い

Error
fun doSomething(arg: List<*>): List<Int> = 
  when(arg[0]) {
    is Int -> ...
    is String -> ...
    else -> ...
  }

Anyが「なんでも受け入れる型」を意味するのに対し、*は「型はわからないけど、今は重要じゃないから伏せ字にしておく」くらいの感覚。

したがって、List<Any>List<*> では、それぞれ次のような挙動になる。

sample
val foo = listOf("bar") //fooの型はList<String>となる。

foo is List<Any> //fooはList<Any>型ではないのでfalse

foo is List<*> //fooは「何かしらのList」型なのでtrue

3. ラムダ式の戻り値 (あるいはブロックとreturnについて)

map,filter,reduce,foldのようなfunctional loopを使うと、綺麗にコードがかける場合が多い。

kotlinでは、これらのfunctional loopは、引数としてラムダ式をとる。

例として、次のような配列を考える。

val sampleList = listOf(1,2,3,4,5)

これらの配列の各要素を2倍にしたいとき、オブジェクト指向言語ではmap関数を使う。

kotlinでは、Iterableインターフェース1が実装されていればmap関数を呼び出すことができる。

目的のラムダ式は次のように書ける。

{ x:Int -> x * 2}

kotlinのマップ関数は、引数にラムダ式を取り、ラムダ式の引数に各要素を代入するので、次のように書けば良い。

val result = sampleList.map {x:Int -> x * 2}
// result == [2,4,6,8,10]

この時returnを書くとmapを抜けてしまう。

意図しない挙動1
sampleList.map {x:Int -> return x * 2}
// 戻り値は4だが、mapよりも上のレベルで return されてしまった。

ラムダ式の->後にはブロック{...}として渡すこともできるが、ブロックの値そのものが返る。

意図しない挙動2
val result = sampleList.map {x:Int -> {
  x * 2
}}  
// => result == [
//  Function0<java.lang.Integer>,
//  Function0<java.lang.Integer>,
//  Function0<java.lang.Integer>,
//  Function0<java.lang.Integer>,
//  Function0<java.lang.Integer>
//  ]

ブロックを渡した際には、ブロック内でreturnするとエラーとなる。

Error
sampleList.map {x:Int -> {
  return x * 2
}}  // エラー => " 'return' is not allowed here "

ラムダ式では、最後の評価値が返される。

sample1
val result = sampleList.map {x:Int -> 
  x * 2
  x * 3
}  // result == [3,6,9,12,15]
sample2
val result = sampleList.map {x:Int -> {
  x * 2
  var a = x + 1
}}  
// => result == [
//  kotlin.Unit,
//  kotlin.Unit,
//  kotlin.Unit,
//  kotlin.Unit,
//  kotlin.Unit
//  ]
//  あくまで「代入した」という処理の結果が返る。

追加でわかったことがあればアップデートします。

  1. ListArrayなど、順番に取り出せるもの

5
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?