Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper implementation of equals(Object) and hashCode() #3

Open
dmfs opened this issue Feb 9, 2017 · 6 comments
Open

Proper implementation of equals(Object) and hashCode() #3

dmfs opened this issue Feb 9, 2017 · 6 comments

Comments

@dmfs
Copy link
Owner

dmfs commented Feb 9, 2017

In order to be able to check Uris (and their components) for equality and put them into Sets or use them as keys in a Map, as a developer I want proper support for equals(Object) and hashCode().


Before we can approach this we need to define the correct behavior. For instance as per RFC 3986, two URIs are considered to represent the same resource if their normalized forms are equal. What does this mean with respect to equals(Object)? Should we normalize the Uris before we compare them or do we leave that to the developer?

In general this could potentially result in a lot of duplicate code, if every implementation has its own equals(Object) and hashCode() methods. So the idea is to create decorators or adapters that add the ability to use equals(Object) and hashCode(). Something like new Hashable(uri) or new Equalable(uri). However since all other objects still have a default implementation of these methods a developer could easily do it wrong without noticing. The current idea to handle this is to throw an UnsupportedOperationException in equals(Object) and hashCode() of objects which do not support hashing and comparison. The behavior would be similar to adding non-hashable objects to a set in Python. This could be achieved by inheriting from an abstract base object class.

@gordom6
Copy link

gordom6 commented Oct 2, 2019

Is this still on the roadmap?

@dmfs
Copy link
Owner Author

dmfs commented Oct 2, 2019

Kind of but in a slightly different way than described above. I want to approach this with an adapter class as described in dmfs/jems#182.

This allows to compare URIs in different ways. For instance, a simple UriKey would just compare the string representations. A more sophisticated version NormalizedUriKey would compare the normalized versions. An even more sophisticated HttpUriKey might be able to figure that https://example.com and https://example.com:443 are equal.

@minorg
Copy link

minorg commented Oct 2, 2019

It would be nice to have a similar approach to toString, if there isn't one already.

@dmfs
Copy link
Owner Author

dmfs commented Oct 3, 2019

@gordom6 what exactly do you mean? There is a Text class which implements CharSequence and supports toString.
So you can do

String uriSring = new Text(uri).toString();

@gordom6
Copy link

gordom6 commented Oct 3, 2019

I must have misunderstood you. I thought you were going to do composition so that the Uri instance has the methods (equals, hashCode toString), instead of relying on code like the above.

So

Uri uri = new ToStringableUri(new LazyUri(...))

Such that uri.toString() works. the equals equivalent would be

Uri uri = new EquatableUri(key, new LazyUri(...))

Such that uri.equals and uri.hashCode have implementations.

I love your library, but having to call a helper to get a string representation is awkward. I was thinking about doing something like the above for toString. I don't understand the nuances of 'equals' and 'hashCode' as well.

@dmfs
Copy link
Owner Author

dmfs commented Oct 4, 2019

I don't consider Text a helper. It's the CharSequence representation of a Uri. My issue with ToStringableUri is, it extends the original Uri contract. In other words, when you hold a Uri you can't be sure whether toString returns what you expect or not.

A Text, on the other hand, is a CharSequence and toString returns the String representation of it, by contract.

The same applies to EqualableUri or HashableUri. It's just too easy to use them incorrectly.
For instance, you can only compare EqualableUri with another EqualableUri. Otherwise equals contract is easily violated (because the equals relation wouldn't be symmetric). Having a dedicated type (and class) which adapts Uris to the equals contact sounds like the better solution to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants