问题
I have an Equatable
class
class Item: Equatable {
var value: AnyObject?
var title: String
init(title: String, value: AnyObject?) {
self.title = title
self.value = value
}
//Equatable
public static func ==(lhs: Item, rhs: Item) -> Bool {
return ((lhs.title == rhs.title) && (lhs.value === rhs.value))
}
}
But I want cast try var value
to Equatable, So that get soft equatable result
if let lValue = lhs.value as? Equatable, // Error
let rValue = rhs.value as? Equatable { // Error
valueEq = (lValue == rValue)
} else {
valueEq = (lhs.value === rhs.value)
}
This code catch compilation error about Generic Equatable
How is I can do correct Equatable for this class?
UPD
I want use my Item
in UITableViewCell
in my storyboard. I Cant create generic UITableViewCell
. And if I try do Item as Generic<T: Equatable>
class, I will be forced to specify the types in my Cells,
var items: [Item<OnlyThisHashableClass>]
but I want use Item in Cells for any objects
回答1:
You can't cast AnyObject
to an Equatable
.
What you can do is define Item
as a generic for which the value
, Wrapped
, must be Equatable
:
class Item<Wrapped: Equatable> {
var title: String
var value: Wrapped
init(title: String, value: Wrapped) {
self.title = title
self.value = value
}
}
extension Item: Equatable {
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.title == rhs.title && lhs.value == rhs.value
}
}
And, let's imagine that you have some class, Foo
, that (a) isn't equatable; (b) is something you want to wrap in an Item
; and (c) you really want to define them to be equatable on the basis of the identity operator, ===
. (I confess, I find that notion, which you call "soft equatable" fairly disturbing notion, but I won't go into that here.)
Anyway, you can just make your class Foo
equatable on the basis of the identity operator:
extension Foo: Equatable {
static func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs === rhs
}
}
Or, if you need to do this for many classes, you could even have a protocol for this identity-equality, and then your non-equatable classes could just conform to that:
protocol IdentityEquatable: class, Equatable { }
extension IdentityEquatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs === rhs
}
}
Then any classes that you want to wrap in an Item
that aren't Equatable
could adopt this identity-equatable behavior with a single line of code each:
extension Foo: IdentityEquatable { }
extension Bar: IdentityEquatable { }
extension Baz: IdentityEquatable { }
As an aside, SE-0143 has been approved and while not part of the language yet, offers the promise of Conditional Conformance in future Swift versions, namely:
class Item<Wrapped> {
var title: String
var value: Wrapped
init(title: String, value: Wrapped) {
self.title = title
self.value = value
}
}
extension Item: Equatable where Wrapped: Equatable {
static func ==(lhs: Item, rhs: Item) -> Bool {
return lhs.title == rhs.title && lhs.value == rhs.value
}
}
In this case, Item
would be Equatable
if and only if the Wrapped
value was Equatable
. This isn't part of the language yet, but looks like it will be in a future version. It is an elegant solution to this problem (though not, admittedly, your "soft equatable" idea).
回答2:
Easy way - class stay NonGeneric, Generic only init
, and in GenericInit create isEquals method
class FieldItem: CustomStringConvertible, Equatable {
let value: Any?
let title: String
private let equals: (Any?) -> Bool
init<Value: Equatable>(title: String, value: Value?) {
func isEquals(_ other: Any?) -> Bool {
if let l = value, let r = other {
if let r = r as? Value {
return l == r
} else {
return false
}
} else {
return true
}
}
self.title = title
self.value = value
self.equals = isEquals
}
//CustomStringConvertible
var description: String { get { return title } }
//Equatable
public static func ==(lhs: FieldItem, rhs: FieldItem) -> Bool {
return ((lhs.title == rhs.title) && lhs.equals(rhs.value))
}
}
来源:https://stackoverflow.com/questions/45990811/anyobject-try-cast-to-equatable