[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