Swiftでプロトコル型に対して===演算子を使いたい時の対処法
記事内に広告を含む場合があります。記事内で紹介する商品を購入することで、当サイトに売り上げの一部が還元されることがあります。
プロトコル型の変数や定数に対して===演算子を使うと「error: binary operator ‘===’ cannot be applied to two ‘プロトコル名’ operands」というエラーが出ることがありますが、その時の対処法について解説したいと思います。
「===演算子」とは?
以下、詳解 Swift 改訂版からの引用です。
まだ説明のない演算子に「===」と「!==」があります。これは参照型の値であるクラスのインスタンスの実体が同一のものかどうかを調べるためのものです。
Swiftには、複数箇所から参照されているインスタンスが同一のものかどうかを調べるための演算子として「===」が用意されています。この演算子はインスタンスの値が等しいかどうか(同値性)ではなく、メモリ上にある同じインスタンスの実体を指しているかどうか(同一性)を調べます。
同一ではないことを調べる演算子は「!==」です。
コードを書いて確認してみましょう。
// Swiftにおいて、Stringはstruct(値型)、NSStringはclass(参照型) let str1 = NSString(string: "hello") let str2 = NSString(string: "hello") let str3 = str1 str1 == str2 // true str1 === str2 // false str1 == str3 // true str1 === str3 // true
str1とstr2は、文字の内容は同じですが、別インスタンスとなっています。
また、str3はstr1と同じインスタンスを参照しています。
プロトコル型の変数や定数に対して===演算子を使ってみる
では、プロトコル型の変数や定数に対して===演算子を使ってみましょう。
protocol PersonalProtocol { var name : String { get set } } class Person : PersonalProtocol { var name : String = "" } let personal1 : PersonalProtocol = Person() let personal2 : PersonalProtocol = Person() // error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands personal1 === personal2
===演算子を使っているところでエラーが出ています。
===演算子の定義について調べてみる
===演算子の定義について確認してみましょう。
public func ===(lhs: AnyObject?, rhs: AnyObject?) -> Bool
引数として、2つのAnyObject?型を受け取っていますね。
以下、詳解 Swift 改訂版からの引用です。
継承関係がないクラスのインスタンス同士を何らかの都合で1つの型で表したい場合もあります。そのような場合、SwiftではAnyObjectという型を利用できます。
AnyObjectの定義についても調べてみましょう。
/// The protocol to which all classes implicitly conform. @objc public protocol AnyObject {
「すべてのクラスが暗黙的に適合するプロトコル」と書かれていますね。
普通のプロトコル型の変数や定数にはstructのインスタンスも代入できるから、===演算子が使えない
以上のことを踏まえると、普通のプロトコル型の変数や定数にはstructのインスタンスも代入できるので、2つのAnyObject型の引数を受け取る===演算子をプロトコル型の変数や定数に対して使用することはできないということですね。
以下、実例を挙げてみます。
protocol PersonalProtocol { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } struct PersonStruct : PersonalProtocol { var name : String = "" } let personal1 : PersonalProtocol = PersonClass() let personal2 : PersonalProtocol = PersonStruct() // error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands personal1 === personal2
structをAnyObjectに適合させてみる
では、structのほうをAnyObjectに適合させてみてはどうでしょうか?
実際にやってみます。
protocol PersonalProtocol { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } // error: non-class type 'PersonStruct' cannot conform to class protocol 'AnyObject' struct PersonStruct : PersonalProtocol, AnyObject { var name : String = "" } let personal1 : PersonalProtocol = PersonClass() let personal2 : PersonalProtocol = PersonStruct() // error: binary operator '===' cannot be applied to two 'PersonalProtocol' operands personal1 === personal2
「error: non-class type ‘PersonStruct’ cannot conform to class protocol ‘AnyObject’」というエラーが出てしまいました。クラスではない型をAnyObjectプロトコルに適合させることはできないみたいです。
プロトコルをAnyObjectから継承させてみる
では、プロトコルをAnyObjectから継承させてみましょう。
protocol PersonalProtocol : AnyObject { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } let personal1 : PersonalProtocol = PersonClass() let personal2 : PersonalProtocol = PersonClass() personal1 === personal2 // false
これでプロトコル型に対して===演算子を使えるようになりました。
なお、PersonalProtorolはAnyObjectを継承しているので、struct型をPersonalProtorolに適合させることはできなくなります。
protocol PersonalProtocol : AnyObject { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } // error: non-class type 'PersonStruct' cannot conform to class protocol 'AnyObject' struct PersonStruct : PersonalProtocol { var name : String = "" }
プロトコルにclassキーワードを記述してもOK
また別の方法として、プロトコルにclassキーワードを記述してもOKみたいです。
以下、詳解 Swift 改訂版からの引用です。
プロトコルはクラス、構造体、列挙型で採用することができますが、値型よりも参照型のデータでの実装を前提としている場合や、継承を利用する場合など、クラスでの実装だけを想定してプロトコルを定義する場合があります。そのような場合、クラスだけが対象であることを明示するために、プロトコル名に続いてコロン「:」を置き、キーワードとしてclassと記述します。
実際にやってみましょう。
protocol PersonalProtocol : class { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } let personal1 : PersonalProtocol = PersonClass() let personal2 : PersonalProtocol = PersonClass() personal1 === personal2 // false
===演算子が使えてますね。
ちなみにclassキーワードを付けたプロトコルは、AnyObjectプロトコルを継承している扱いになるようです。
protocol PersonalProtocol : class { var name : String { get set } } class PersonClass : PersonalProtocol { var name : String = "" } let personal1 : PersonalProtocol = PersonClass() personal1 is AnyObject // true
@akio0911はこう思った。
エラーが出た時は「なんでプロトコルに===演算子が使えないの!?」と思ったのですが、いろいろと調べてみるときちんと筋の通った仕組みになっていることが分かりますね。
Swiftの細かい仕様について学ぶには、詳解 Swift 改訂版が便利です。
関連記事
この記事が気に入ったら「いいね!」しよう
Twitterで更新情報をゲット!