2016年5月31日

C# 文字列リテラル(補間文字列・逐語的文字列)まとめ

C# 6.0 から補間文字列が追加されて、文字列リテラルの種類が 4 つに増えている。
MSDN にまとめた記事が無いので(たぶん)、自分用まとめ

書式 バックスラッシュ
エスケープ
改行 補間式 その他の
エスケープ
標準 "regular"
逐語的 @"verbatim
string"

"" → "

補間 $"1+1={1+1}"

{{ → {

}} → }

補間
逐語的
$@"interpolated
{"and"}
verbatim"

"" → "

{{ → {

}} → }


また、補間文字列の補間式の中に各種文字列リテラルを書くことが可能。
つまり、補間文字列のネストも可能。(実用性は無さそうだけど)
ただし、改行を許可しない補間文字列の中に、逐語的文字列や補間逐語的文字列を書いても改行を含めることはできない。(コンパイルエラー)

// 逐語的(改行無し) in 補間 : OK
var nested1 = $"this is {@"verbatim string"}";

// 逐語的(改行有り) in 補間逐語的 : OK
var nested2 = $@"this is {@"verbatim
string"}";


// 逐語的(改行有り) in 補間 : ERROR
var nested_error = $"this is {@"verbatim
string"}";

参考URL

2016年5月25日

Kotlin から見た Java の getter / setter (getXXX / isXXX / setXXX)

Calling Java code from Kotlin - Getters and Setters の補足みたいな話。
Kotlin 1.0.2

Kotlin から Java のコードを呼ぶ際、getter / setter (getXXX / isXXX / setXXX メソッド) はプロパティとして扱える。
詳細なルールとしては、
getter
  • get{X} または is{X} で始まる引数無し・戻り値ありのインスタンスメソッド
    {X} は小文字と解釈されない文字、日本語でもOK
  • getXXX / isXXX でプロパティ名の規則が異なる
    • getHoge メソッド → hoge プロパティ
    • isHoge メソッド → isHoge プロパティ
  • isXXX の戻り値の型は問わない (Boolean 以外でも可)
setter
  • set{X} で始まる引数 1 つのインスタンスメソッド
  • 戻り値の有無は問わない (あってもいい)
  • 対になる getter が必要 (Kotlin は今の所 set-only プロパティをサポートしてないため)

[ルールのサンプル (Java)]

// Kotlin でプロパティになるケース (メソッドとして使用できない)
public class PropertySample {
    public String getName() { /* 略 */ }
    public void setName(String name) { /* 略 */ }
    // [Kotlin] public final var name: String!
    // getter/setter があるので var プロパティ

    public Boolean isKey() { /* 略 */ }
    public void setKey(Boolean key) { /* 略 */ }
    // [Kotlin] public final var isKey: Boolean!
    // getter が isXXX の場合、プロパティ名は同じ(isXXX)

    public Boolean getKey() { /* 略 */ }
    // [Kotlin] public final var key: Boolean!
    // getter が isXXX と getXXX の 2 つ存在する場合、プロパティも 2 つになる
    // setter は共通 (両方のプロパティから使用される)

    public int getId() { /* 略 */ }
    // [Kotlin] public final val id: Int
    // getter のみは val プロパティ

    public String getあ() { /* 略 */ }
    public String setあ(String あ) { /* 略 */ }
    // [Kotlin] public final var あ: String!
    // 日本語可、setter に戻り値があっても可
}

// Kotlin でプロパティにならないケース (メソッドとして使用できる)
public class NotPropertySample {
    // setter のみ
    public void setNum(int num) { /* 略 */ }

    // 小文字スタート
    public int geta() { /* 略 */ }

    // static メソッド
    public static int getStatic() { /* 略 */ }

    // プロパティ名がかぶる
    public String getUrl() { /* 略 */ }
    public String getURL() { /* 略 */ } // 全部大文字の場合、プロパティ名は小文字になる
}

雑記

isXXX は混乱の元になりそうなのであまり使いたくないところ。
でも Kotlin から Java を呼び出すケースって、おそらく既存ソースの再利用で isXXX がある可能性もそれなりにあって・・・

ちなみに Java から Kotlin のプロパティを使用する際は、逆のイメージ。
  • XXX プロパティ → getXXX / setXXX メソッド
  • isXXX プロパティ → isXXX / setXXX メソッド

2016年3月23日

Kotlin null 許容型 (nullable) の外し方(スマートキャスト)

Kotlin の null 許容型 (nullable type) の変数は、if 等の制御文を挟むことで、null 非許容型 (non-null type) に自動でキャストされる。
null 許容型はコンパイル時にしか存在しないので、バイトコードにキャスト処理が入るわけではない。
IntelliJ IDEA だと、自動キャストされた変数はハイライトされて、マウスオーバーすると「Smart cast to 型名」がポップアップされるので分り易い。

ここでは、どんな制御文で null 許容型からのスマートキャストが可能か(コンパイラがどこまでやれるのか)、色々試してみる。
Kotlin 1.0.1 なので、バージョンが上がると挙動が変わるかも。

1 変数の場合

  1. if で null 除外
    
    fun ifNotNull(x: Any?) {
        if( x != null ) println(x.javaClass)
    }
    
    基本。
  2. if で null の場合、return
    
    fun ifNullReturn(x: Any?) {
        if( x == null ) return
        println(x.javaClass)
    }
    
    (1) の逆バージョン。
  3. when で null 除外
    
    fun whenNotNull(x: Any?) {
        when( x ){
            null -> {}
            else -> println(x.javaClass)
        }
    }
    
    (1) の when バージョン。if 使った方がいい。
  4. when で null の場合、return
    
    fun whenNullReturn(x: Any?) {
        when( x ){
            null -> return
            else -> {}
        }
        println(x.javaClass)
    }
    
    (2) の when バージョン。これも if 使った方がいい。
    また、else を省略するとコンパイルエラー( .javaClass で)。
  5. エルビス演算子 ?: で null の場合、return
    
    fun elvisNullReturn(x: Any?) {
        x ?: return
        println(x.javaClass)
    }
    
    (2) のエルビス演算子バージョン。
    少し魔術っぽいけど、慣れたら問題ない・・・? タイプ量が少なくなるから使いそう。
    return の代わりに例外 throw も可。
  6. ぬるぽ演算子 !! で null の場合、例外
    
    fun nullpo(x: Any?) {
        x!!
        println(x.javaClass)
    }
    
    ほとんどの場合、バッドプラクティス。(ぬるぽ演算子の使い所は難しい・・・)
    !! 演算子は特に名前が無いようなので、"ぬるぽ演算子"の呼び方は非公式
  7. while で null 除外
    
    fun whileNotNull(x: Any?) {
        while( x != null ){
            println(x.javaClass)
            break
        }
    }
    
    (1) の while バージョン。使い所はあまり無い気がするけど、一応可能。
  8. while で null の場合、return
    
    fun whileNullReturn(x: Any?) {
        while( x == null ) return
        println(x.javaClass)
    }
    
    (2) の while バージョン。使い所なんて無い気がするけど、一応可能。
  9. [NG] if-true ネストで if-return
    
    fun ifNullReturnIfNest(x: Any?) {
        if( true ){
            if( x == null ) return
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    ありえないコードは、ネストの中まで見てくれない模様。
  10. while-true ネストで if-return
    
    fun ifNullReturnWhileNest(x: Any?) {
        while( true ){
            if( x == null ) return
            break
        }
        println(x.javaClass)
    }
    
    if と異なり while ならネストの中まで見てくれる・・・。
    while の条件を変数にすると、コンパイルエラー。
    たぶん、while-true がありえるかもしれないコードだからだと推測。
  11. [NG] when-true ネストで if-return
    
    fun ifNullReturnWhenNest(x: Any?) {
        when {
            true -> if( x == null ) return
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    これもありえないコードなので不可。when に else があっても不可。
  12. [NG] with 関数のラムダ式で if-return
    
    fun ifNullReturnWithScope(x: Any?) {
        with( x ){
            if( x == null ) return // この return は ifNullReturnWithScope を抜ける
    
            // 本来、with 内で x にアクセスする際は this を使う
        }
        println(x.javaClass) // コンパイルエラー
    }
    
    VB でお馴染みの With だが、Kotlin では関数 + ラムダ式になっている。(組み込みの構文ではない。)
    with 関数はインライン展開されるけど、さすがに関数の中まで見てくれない。

2 変数の場合

  1. if で null 除外
    
    fun ifNotNull(x: Any?, y: Any?) {
        if( x != null && y != null ){
            println(x.javaClass)
            println(y.javaClass)
        }
    }
    
    単純な複合条件は可能。
    引数無し when でも同様に可能。
  2. if で null の場合、return
    
    fun ifNullReturn(x: Any?, y: Any?) {
        if( x == null || y == null ) return
        println(x.javaClass)
        println(y.javaClass)
    }
    
    (1) の逆バージョン。
    引数無し when (else 有り) でも同様に可能。
  3. if-else-if で順番に null 除外
    
    fun ifNotNullEach(x: Any?, y: Any?) {
        if( x == null ){
            // x は null、y は null かも
        } else if( y == null ){
            // x は 非 null、y は null
            println(x.javaClass)
        } else {
            // x、y ともに非 null
            println(x.javaClass)
            println(y.javaClass)
        }
    }
    
    一度 null を除外すれば、次のブロックでも有効。
    引数無し when でも同様に可能。
  4. [NG] if-else-if で順番に複合条件で null 除外
    
    fun ifNotNullComplex(x: Any?, y: Any?) {
        if( x == null && y == null ){
            // x、y ともに null
        } else if( x != null && y == null ){
            // x は 非 null、y は null
            println(x.javaClass)
        } else if( x == null && y != null ){
            // x は null、y は 非 null
            println(y.javaClass)
        } else {
            // x、y ともに非 null のはず
            println(x.javaClass) // コンパイルエラー
        }
    }
    
    コンパイラが複雑になるから見てないのだと推測。特に変数が増えた場合やばそう・・・
    人間が見ても else ブロックで null 除外されていることは、分かりづらいと思う。

2016年3月10日

Kotlin null 許容型 (nullable) はコンパイル時だけの存在

Kotlin の目玉機能の 1 つ「null 安全」のために null 許容型 (nullable type) が存在するが、これはコンパイル時だけの存在で、実体クラスが定義されているわけではない。
C# / VB.NET 経験者なら Nullable<T> 相当のクラスがあるのか?と思うかもしれないが、そんなものは無い。

たぶん、Java コードとの共存 + 実行時のパフォーマンスが考慮された仕様になっている。

一応、下記で検証可能。

fun main(args: Array<String>) {
    printType<Int>()
    printType<Int?>()
    // Int?::class だとコンパイルエラー (直接 println できない)
}

inline fun <reified T> printType() {
    println(T::class)
}

//[結果]
// class kotlin.Int
// class kotlin.Int
※ 2 行目と 3 行目で生成されるバイトコードは同じ

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0

2016年2月12日

Kotlin 遅延評価型と先行評価型のコレクション操作関数

"ことりん" の名前が気に入ったので色々調査中・・・
(・8・) × >ω</

Kotlin のコレクション操作関数(C# の LINQ to Object、Java の Stream API 相当)は、1.0.0-rc-1036 時点で stdlib に 2 種類用意されている。
Sequence インターフェース の拡張関数(遅延評価型)と、Iterable インターフェース の拡張関数(先行評価型)である。
※ スカラ値を返す関数(any, count, max 等)は両方とも先行評価型

よって、正確には、LINQ to Object や Stream API 相当になるのは、Sequence 拡張関数の方になる。

[Sequence と Iterable 拡張関数の比較]

fun main(args: Array<String>) {
    val source = 0..3
    lazyEvaluationMethods(source)
    println()
    eagerEvaluationMethods(source)
}

// 数字リストから偶数のみを抽出して 2 倍する

fun lazyEvaluationMethods(source : Iterable<Int>) {
    var result = source.asSequence().filter {
        println("sequence.filter : $it")
        it % 2 == 0
    }.map {
        println("sequence.map : $it")
        it * 2
    }.toList()
    println(result)
}

fun eagerEvaluationMethods(source : Iterable<Int>) {
    var result = source.filter {
        println("iterable.filter : $it")
        it % 2 == 0
    }.map {
        println("iterable.map : $it")
        it * 2
    }
    println(result)
}
[結果]
sequence.filter : 0
sequence.map : 0
sequence.filter : 1
sequence.filter : 2
sequence.map : 2
sequence.filter : 3
[0, 4]

iterable.filter : 0
iterable.filter : 1
iterable.filter : 2
iterable.filter : 3
iterable.map : 0
iterable.map : 2
[0, 4]

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0-rc-1036

雑記

Sequence と Iterable の違いを知ってないと割と危険な実装をしてしまいそう・・・
Iterable 拡張関数の方は戻り値が kotlin.collections.List 型なのですぐに気付くはず。
その List の実装は、今のところ java.util.ArrayList が利用されている。

2016年2月10日

Kotlin ジェネリクス 型引数の情報を実行時に取得

Kotlin 正式版がそろそろリリースされそうなので・・・
GitHub - temp-impl/spring-mvc-singleton-vs-prototype_kotlin を作った時の調査メモ。)

Kotlin も JVM 言語なので、コンパイル時型消去の呪いからは逃れられない。
が、inline と reified を使うことで、関数・メソッドなら型引数の情報を実行時に使用可能になる。

[型引数使用サンプル]

fun main(args: Array<String>) {
    printType<String>()
}

inline fun <reified T> printType() {
    println(T::class)
}

//[結果]
// class kotlin.String

//[補足]
// 上記結果を得るためには kotlin-refrect.jar も必要
// 無くてもエラー無しで実行できるが、下記結果になる
// class java.lang.String (Kotlin reflection is not available)
[上記のバイトコード(一部)]

   L2
    LINENUMBER 8 L2
    LDC Ljava/lang/String;.class
    INVOKESTATIC kotlin/jvm/internal/Reflection.getOrCreateKotlinClass (Ljava/lang/Class;)Lkotlin/reflect/KClass;
    ASTORE 1
    NOP
   L3
    LINENUMBER 11 L3
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V

inline というキーワードから想像が付くと思うが、関数の中身がインライン展開されている。
そのため、型引数の情報も当然使用可能。

ちなみに Java クラスの情報が欲しい場合

inline fun <reified T : Any> printJavaType() {
    println(T::class.java)
}

//[結果] (上と同じ main)
// class java.lang.String

//[補足]
// 型引数のデフォルト上限は Any? で Java に無いクラスなので、Any 上限を指定する必要がある
// .java は KClass<T : Any> の拡張プロパティ

検証環境

  • jdk 1.8.0_74
  • Kotlin 1.0.0-rc-1036

雑記

inline 関数の型引数はデフォルト reified でも良さそうと思ったが、たぶん Kotlin は(型引数使用を)明示していくスタイルなのだろう。
(コンパイラの都合もあるかもしれない・・・)

Scala の implicit + ClassTag みたいな仕組みはおそらく導入されない。
Comparison to Scala - Kotlin Programming Language を読むと implicit は導入する気が無さそうなので。