J'essaie d'implémenter l'opérateur ==
(à partir de Equatable
) dans une classe de base et ses sous-classes dans Swift 3. Toutes les classes ne seront utilisées que dans Swift, je ne souhaite donc pas impliquer NSObject
ou le protocole NSCopying
.
J'ai commencé avec une classe de base et une sous-classe:
class Base {
var x : Int
}
class Subclass : Base {
var y : String
}
Maintenant, je voulais ajouter Equatable
et l'opérateur ==
à Base
. Cela semble assez simple. Copiez la signature de l'opérateur ==
à partir de la documentation:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
Jusqu'ici tout va bien. Passons maintenant à la sous-classe:
class Subclass : Base {
static override func == (lhs: Base, rhs: Base) -> Bool {
return true
}
}
Mais cela entraîne une erreur:
La fonction opérateur remplace une fonction opérateur 'finale'
D'ACCORD. Après quelques recherches (j'apprends toujours Swift 3), j'apprends que static
peut être remplacé par class
pour indiquer que la méthode type peut être remplacée.
Je tente donc de remplacer static
par class
dans Base
:
class Base : Equatable {
var x : Int
class func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
Mais cela entraîne une nouvelle erreur:
L'opérateur '==' déclaré dans la classe non finale 'Base' doit être 'final'
Pouah. C'est beaucoup plus compliqué que cela ne devrait être.
Comment implémenter correctement le protocole Equatable
et l'opérateur ==
dans une classe de base et une sous-classe?
Après de nombreuses recherches et quelques essais et erreurs, j'ai finalement trouvé une solution efficace. La première étape a été de déplacer l'opérateur ==
de l'intérieur de la classe vers la portée globale. Cela corrigeait les erreurs concernant static
et final
.
Pour la classe de base, ceci est devenu:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
Et pour la sous-classe:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
return true
}
class Subclass : Base {
var y : String
}
Maintenant, il ne reste plus qu'à comprendre comment appeler l'opérateur ==
de la classe de base à partir de l'opérateur ==
de la sous-classe. Cela m'a conduit à la solution finale:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
Cette première instruction if
entraîne l'appel de l'opérateur ==
dans la classe de base.
La solution finale:
Base.Swift:
func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
class Base : Equatable {
var x : Int
}
Sous-classe.Swift:
func == (lhs: Subclass, rhs: Subclass) -> Bool {
if lhs.y == rhs.y {
if lhs as Base == rhs as Base {
return true
}
}
return false
}
class Subclass : Base {
var y : String
}
Je sais que la question a été postée il y a longtemps, mais j'espère que ma réponse aidera.
TLDR - Au lieu d'essayer de remplacer ==
, vous fournissez une méthode de comparaison personnalisée, faites appeler ==
et remplacez la méthode de comparaison personnalisée si nécessaire.
Alors tu as dit
Toutes les classes ne seront utilisées que dans Swift, je ne souhaite donc pas utiliser
NSObject
ni le protocoleNSCopying
.
Mais si vous étiez dans la sous-classe NSObject
, comment allez-vous écrire votre méthode de comparaison personnalisée? Vous annulerez isEqual(Any?)
, non? Et si vous essayez de vous conformer au protocole Equatable
dans votre sous-classe, le compilateur se plaindra de la "conformité redondante au protocole Equatable
" car NSObject
est déjà conforme à Equatable
.
Maintenant, cela nous donne quelques indications sur la façon dont NSObject
traite ce problème - il fournit une méthode de comparaison personnalisée isEqual(Any?)
, appelez-le dans ==
et ses sous-classes peuvent le remplacer si nécessaire. Vous pouvez faire la même chose dans votre propre classe de base.
Sans plus tarder, faisons quelques expériences (dans Swift 4). Définir des classes
class Grandpa: Equatable {
var x = 0
static func ==(lhs: Grandpa, rhs: Grandpa) -> Bool {
return lhs.isEqual(to: rhs)
}
func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Grandpa.self else {
return false
}
let value = object as! Grandpa
return x == value.x
}
}
class Father: Grandpa {
var y = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Father.self else {
return false
}
let value = object as! Father
return x == value.x && y == value.y
}
}
class Son: Father {
var z = 0
override func isEqual(to object: Any?) -> Bool {
guard object != nil && type(of: object!) == Son.self else {
return false
}
let value = object as! Son
return x == value.x && y == value.y && z == value.z
}
}
Et écris du code de test
let grandpa1 = Grandpa()
let grandpa2 = Grandpa()
let grandpa3: Grandpa? = nil
let grandpa4: Grandpa? = nil
let father1 = Father()
let father2 = Father()
let father3 = Father()
father3.y = 1
let son1 = Son()
let son2 = Son()
let son3 = Son()
son3.z = 1
print("grandpa1 == grandpa2: \(grandpa1 == grandpa2)")
print("grandpa1 == grandpa3: \(grandpa1 == grandpa3)")
print("grandpa3 == grandpa4: \(grandpa3 == grandpa4)")
print("grandpa1 == father1: \(grandpa1 == father1)")
print("father1 == father2: \(father1 == father2)")
print("father1 == father3: \(father1 == father3)")
print("son1 == son2: \(son1 == son2)")
print("son1 == son3: \(son1 == son3)")
Exécutez-le et vous devriez obtenir
grandpa1 == grandpa2: true
grandpa1 == grandpa3: false
grandpa3 == grandpa4: true
grandpa1 == father1: false
father1 == father2: true
father1 == father3: false
son1 == son2: true
son1 == son3: false
Après les autres réponses, j'ai trouvé ceci:
class Base : Equatable {
var x : Int
static func == (lhs: Base, rhs: Base) -> Bool {
return lhs.x == rhs.x
}
}
class Subclass : Base {
var y : String
static func == (lhs: Subclass, rhs: Subclass) -> Bool {
return lhs.y == rhs.y && (lhs as Base) == (rhs as Base)
}
}