[swift-users] Subtract a set of a subclass?

Jordan Rose jordan_rose at apple.com
Thu Sep 1 18:32:52 CDT 2016


The Liskov substitution principle <https://en.wikipedia.org/wiki/Liskov_substitution_principle> says that a B should always be able to be treated like an A. Your Set<A> may already contain Bs, even without them ever being statically typed as B. If you think A and B are unrelated types, you should be using composition rather than inheritance.

If a subclass overrides hashValue, they must be in a position to affect == as well, and it must work no matter which object is on the left-hand side. NSObject does this by having == call the isEqual(_:) method, but you still need to design your class hierarchy and isEqual(_:) methods carefully.

Jordan


> On Sep 1, 2016, at 16:28, Zhao Xin <owenzx at gmail.com> wrote:
> 
> I believe if B inherits A, they are not the same type. So the rule doesn't apply here.
> 
> Zhaoxin
> 
> On Fri, Sep 2, 2016 at 7:02 AM, Nick Brook <nrbrook at gmail.com <mailto:nrbrook at gmail.com>> wrote:
> Hi Jordan,
> 
> Thanks for the advice.
> 
> What if a subclass does implement hashValue differently? It seems you are saying a subclass should never override hashValue? Should Set not compare elements with == instead of hashValue?
> 
> Thanks
> Nick
> 
> M: +44 (0)7986 048 141 <tel:%2B44%20%280%297986%20048%20141>
> W: http://nickbrook.me <http://nickbrook.me/>
> 
>> On 1 Sep 2016, at 23:55, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>> 
>>> 
>>> On Sep 1, 2016, at 15:44, Zhao Xin via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
>>> 
>>> Hi Nick,
>>> 
>>> Glad to help.
>>> 
>>> but when using third party classes I don’t know if the hash values are comparable
>>> 
>>> You can create an extension with a convenient init(:), which creates a new instance of  the super class basing on the instance of the sub class. That will guarantee the subtraction. Below code works in Xcode 7.3.1 with Swift 2.2.
>>> 
>>> import Foundation
>>> 
>>> func ==(lhs: Foo, rhs: Foo) -> Bool {
>>>     return lhs.id == rhs.id
>>> }
>>> 
>>> class Foo:Hashable {
>>>     let id:Int
>>>     var hashValue: Int {
>>>         return id
>>>     }
>>>     
>>>     required init(_ id:Int) {
>>>         self.id = id
>>>     }
>>> }
>>> 
>>> class Bar:Foo {
>>>     override var hashValue: Int {
>>>         return id * 5
>>>     }
>>> }
>>> 
>>> var fooSet:Set<Foo> = [Foo(10), Foo(9), Foo(8), Foo(7)]
>>> var barSet:Set<Bar> = [Bar(8), Bar(7), Bar(6), Bar(5)]
>>> 
>>> //fooSet.subtract(barSet) // error: cannot invoke 'subtract' with an argument list of type '(Set<Bar>)'
>>> fooSet = fooSet.subtract(barSet as Set<Foo>) // works, but not what we want
>>> fooSet.forEach { print("\($0.dynamicType), id:\($0.id)") }
>>> /*
>>>  Foo, id:7
>>>  Foo, id:10
>>>  Foo, id:9
>>> */
>> 
>> This isn't really a sensible thing to do. The rules for Hashable require that `a == b` implies `a.hashValue == b.hashValue`, and `a.hashValue != b.hashValue` implies `a != b`. If you break these rules you're going to have problems no matter what static types you're using.
>> 
>> Upcasting from Set<Bar> to Set<Foo> is the most concise way to solve this problem.
>> 
>> Jordan
> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160901/9455c16e/attachment.html>


More information about the swift-users mailing list