31037


class BaseClass{}  
class AClass : BaseClass{}  
class BClass : BaseClass{}

//var b : BaseClass = AClass()  
var b : BaseClass = BClass()

if let p = b as? AClass{  
print("OK:(type(of:p))")  
}else{  
print("else")  
}

switch b{  
case let p as AClass:  
print("OK:(type(of:p))")  
default:  
print("default")  
break  
}

if文では、as? switch文では、asと記述しますが、何かしっくりきません。  
それでいて、if文のpは、Optional型とはならない。  
if文も asでもいいのではと思ってしまいます。

たいてい、そこには理由があるのですが、ご指摘お願いします。


CC BY-SA 3.0
2016-12-09T01:43:15.900
31033
2016-12-09T02:48:55.460
11148
question
swift swift3
if文、switch文でas演算子の使い方が変わる？


`if`文と`switch`文の違いというよりはオプショナルバインディングとパターンマッチングの違いと言った方が良いでしょう。

カッコを付けた方が少しわかりやすくなるのではないかと思います。

オプショナルバインディングの方は、

[code]

    if let p = (b as? AClass) {
        print("OK:\(type(of:p))")
    } else {
        print("else")
    }
    
[/code]

と言うことになります。`(b as? AClass)`というのがOptionalの値を返す式で、その値が非`nil`であれば`let
p`と言う変数宣言を成立させるという働きになります。`b as
AClass`と言う式は、`b`の値が安全かつ確実に`AClass`に変換可能である場合にしか使えず、結果もOptionalにはなりませんから、この構文では使えません。

一方パターンマッチングの方はこんなカッコの付け方になります。

[code]

    switch b {
    case (let p as AClass):
        print("OK:\(type(of:p))")
    default:
        print("default")
        break
    }
    
[/code]

ここで`(let p as AClass)`は一つのパターンであり、`b`の値がそのパターンにマッチするなら`let
p`での変数宣言を成立させるという形になります。

Swift言語の仕様としての[pattern](https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/Patterns.html#//apple_ref/doc/uid/TP40014097-CH36-ID419)は、かなり汎用の概念で、Optional型が非`nil`の値を持つかどうかの判定を含めた幅広い使い方ができます。(あまり良いサンプルがネット上ではすぐには見つかりませんが。)

と言うわけで「if文も asでもいいのでは」と言うのを広めに解釈して、「`if`文もパターンマッチングだけでいいのでは」と言う考え方もあったとは思いますが。

  * 当初のSwiftでは`if`文ではパターンマッチングを使えなかった

  * Swiftの特徴的概念であるOptionalの判定だけは`if`文でもできるようにしたかった

と言った事情で、オプショナルバインディングに特化したif-letの構文が考えられたのではないかと思います。

* * *

現在のSwift(確か2.0以降?)では、`if`文でもパターンマッチングが使えるようになりましたので、どうしても`switch`文に近い使い方をしたければ、こんな風に書くこともできます。

[code]

    if case (let p as AClass) = b {
        print("OK:\(type(of:p))")
    } else {
        print("else")
    }
    
[/code]

(`case`の後のパターンを囲むカッコは、上の説明と整合性を持たせるために入れてあるもので、無くても動きます。)

* * *

オプショナルバインディングとパターンマッチングの両方が`if`文で使えると、どちらを使った方が良いか迷う場合もありますが、「非`nil`かどうかを判定したいだけ」と言う場合にはやはりオプショナルバインディングの方が便利でしょう。

[code]

    if let s = someMethod() {//<- someMethod()は`String?`型の値を返すものとする
        doSomething(s) //<- 非nilの`String`型が必要なメソッド
    }
    
[/code]

これをif-caseで書くと、

[code]

    if case let s as String = someMethod() {//<- 非nilかどうかの判定でいちいちそれが`String`であることを示さないといけない
        doSomething(s)
    }
    
[/code]

とか、

[code]

    if case let s? = someMethod() {//<- Stringなんて示さなくても良いが、まだあまり一般的でなくわかりにくいと思う人が多い
        doSomething(s)
    }
    
[/code]

とかになります。

(Xcode 8.1のPlaygroundで試したら`if case let s as String ...`に変な警告が出ますが、Xcode
8.1/Swift 3.0.1のバグだと思います。)

同じことが何通りもの方法でできるのはわかりにくい場合もありますが、「型判定と条件付きバインディングを同時に行いたい場合にオプショナルバインディングを使いたければ、結果がOptionalになる式を作らないといけないので`as?`を使っている」と思えばわかりやすくなるのではないか…と思ったんですが、説明が長すぎてわかりやすいかどうかは微妙ですね。

とにかく **if-letはオプショナルバインディング(値が非`nil`かどうかの判定が主眼)なんだ**と覚えてください。

* * *

型判定を行う場合に`if let typedValue = value as? AType`と言う書き方をするのは、if-
caseがなかった頃に定着したイディオムですが、「非`nil`かどうかを判定したいんじゃ無くてデータ型の判定をしたいんだ」と言う場合にはパターンマッチングを使う方がより直感的で良いのかもしれません。

Swiftは歴史の浅い言語で、しかも言語仕様がバージョンアップのたびに大きく変化しています。「これからのSwiftでの標準的な書き方」は、これからSwiftを使う開発者が見つけて定着させていけば良いのだと思います。

また長くなってしまいましたが、何かお気付きの点があれば、コメント等でお知らせください。


CC BY-SA 3.0
2016-12-09T02:48:55.460
31037
2016-12-09T02:48:55.460
13972
31033
answer