プロパティの(価)値
前回予告したとおり、今回はカジュアルな話題です。題して“The value of the property”。「プロパティの値」とも「プロパティの価値」とも邦訳できますが、その双方の意味を込めています。
しかしそもそも「プロパティ」って何でしょう?
iOSの辞書によると(図1)、「財産」「不動産」「属性」「所有権」そして一周回って「プロパティ」と出てきますが、いずれも共通しているのは「固有の何か」。たとえばファイル。ファイルの内容が「値」なら、ファイル名や更新日時などのメタデータは「プロパティ」。「固有値」という訳がよさそうなのですが、この言葉は数学における“eigenvalue”という言葉に「取られて」しまったおかげか、ITの世界では「プロパティ」というカタカナで落ち着いたようです。
値の中の値
それではSwiftにおけるプロパティとはいったいなんでしょうか? 断言する前に実例を見てみましょう。Playgroundで次を入力してみてください。
それぞれの値を文字列にしたものが""
に囲まれて表示されているはずです。これがそれぞれの値(value)に対する.description
という名のプロパティ(property)で、英語のdescription=説明という名のとおり、それぞれの値の説明になっています。
今度は.description
を.hashValue
にしてみましょう。String
やInt
やDouble
ではこのようにInt
の値が出てきますが……、
Array
やDictionary
だとエラーになります(図2)。
どんな値がどんなプロパティを持つかは、型(type)によって決まります。
今そこにあるプロパティ・その場で作られるプロパティ
プロパティは型によって決まる。具体的にその様子を見てみましょう。
Point
というStruct
を作ったうえで使ってみます。
.x
と.y
というプロパティが存在し、初期化したとおりの値となっています。これをストアド・プロパティ(stored property)と言います。Cの構造体(struct)のメンバに相当します。
このPoint
に原点(0,0)から距離を返す.distance
というプロパティを付け加えてみましょう。
実行結果は次のとおり。
メモリにそのまま保持されているストアド・プロパティに対し、演算された結果はコンピューテッド・プロパティ(computed property)と言います。
「あれ? これってメソッド(method)じゃね?」と思った読者のあなたは鋭い。実は本質的に同じことなのです。
ここで.getDistance()
の()
を取ってみましょう。エラーにはならず、() -> Double
と出てくるはずです。
そうなのです。Swiftではメソッドもまた単なる関数型のストアド・プロパティに過ぎず、コンピューテッド・プロパティは()
を付けずに実行されるメソッドにすぎないのです。
しかし、Swiftのコンピューテッド・プロパティにはそれ以上のことができます。ストアド・プロパティを上書きすることもできるのです。
.distance
の定義を次のように変えてみます。
すると……、
これは、次と等価と言えるでしょう。
ほかの言語でも、値を取得するプロパティ(や言語によっては属性[attribute])をgetter、値を変更するプロパティをsetterと言いますが、Swiftのプロパティはその双方に対応しています。どんな場合にどちらを用いるべきかというのはスタイルの問題になりますが、Swiftはプロパティとメソッドの違いは()
の有無に過ぎず、どちらもプロパティという立場をとっています。
添字もsubscriptという名のプロパティ
配列(array)や辞書(dictionary)から要素を取り出すとき、多くの言語では添字(subscript)で要素を指定します。これはSwiftも同様です。
しかしよく考えてみると、これも一種のgetter/setterです。一般化できないのでしょうか? もちろんできます。そう、Swiftなら。
例として、Int
をBool
の配列として操作できるようにしてみます。
実行してみます。
Swiftではsubscript
というメソッド=プロパティを定義することによって、ほぼどんな型でも添字が使えるようになります。別の言い方をすると、添字もまたメソッドの構文糖衣(syntactic sugar)にすぎないということです。
ちなみにsubscript
というのはSwiftではキーワード扱いなので、
と書くことはできません。エラーになるべきなのですが、Xcode 9.2/Swift 4.0.3の現在は、図3のようになります。残念!
型もまた値。そして値には……
Rubyではクラス自体もオブジェクトですが、Swiftの型もまた値(value)になっています。しかしそのまま値として使えてしまうと何かと不便なので、値として使いたいときには.self
というプロパティを使います。
何でもはできないわよ。できることだけ
こうしてみると、プロパティは型をも含めたSwiftのすべての値に存在するように見えます。ほぼ正解なのですが重大な例外が3つ存在します。
1つは、Optional
。
当然ながら、?
か!
でunwrapせねばならず、そしてunwrapされた値はもはやOptional
ではありません。Optional
の存在意義を考えれば、これは当然とも言えます。
もう1つは、Any
。
何でも入るかわりにそのままでは何もできません。何もできないように組込みプロパティが存在しないだけではなく、extension
することも禁止されています。使うときにはasなどで中身を取り出すという意味で、Optional
と同様に自然です。
そして最後に総称型(generic types)。以前の連載でも触れましたが、
を
と書き直すことはできません。別の言い方をすると、Swiftの総称型には値が存在せず、具体的な型となってはじめて値を持つのです。[42]
の型はArray
ではなくArray<Int>
であり、["swift":4.1]
の型はDictionary
ではなくDictionary<String,Double>
であることを考えれば、これまた当然とも言えます。
まとめ
値があるところにプロパティがあり、型自身すら値を持つSwiftにおけるプログラミングとは、型にプロパティを植えることであると言っても過言ではありません。というわけでまとめます。
Any
およびOptional
、そして総称型以外のすべての値(value)にプロパティ(property)を持たせられる
- プロパティもまた値
- 型(type)自体も値(value)
- プロパティは値をメモリに保持するストアド・プロパティ(stored property)だけではなく演算結果を返すコンピューテッド・プロパティ(computed property)がある
- メソッド(method)は引数を0個以上取るコンピューテッド・プロパティ
- 添字(subscript)は引数を1個以上取るメソッド
.subscript
という名のメソッドは禁則事項
(ほぼ)すべての値にプロパティを持たせられることは、静的型付けのSwiftが動的言語のようにカジュアルに書ける理由の1つです。いや、それ以上かもしれません。静的であるがゆえに型がわかった時点でどんなプロパティが存在するかは確定し、確定しているがゆえにXcodeでもREPLでもプロパティを補完することができるのですから。
次回もまた、Swiftがどうやって静的言語と動的言語のいいところどりをしているかを見ていくことにしましょう。
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
- 第2特集
「知りたい」「使いたい」「発信したい」をかなえる
OSSソースコードリーディングのススメ
- 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
- 短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
- 短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
- 短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)