-
-
Notifications
You must be signed in to change notification settings - Fork 639
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
[type hierarchy] Need for semi-sealed interfaces #1825
Comments
Why not make List an abstract class instead of an interface. By giving the abstract List class an private constructor, we can ensure that only inner classes can extend it, and thereby guaranty that all instances of the List class is immutable. Currently I consider List as not being immutable, because when I create a method that takes a List, client could just as well pass in there own mutable implementation. This is fine on the generel interfaces, but the concrete implementations (as List is one of), should be able to completely guaranty immutability. |
@Kreinoee You are right. To the time we initially designed the interfaces the future direction wasn't clear, e.g. adding mutable counterparts, parallel collection views, ... But from the user-site viewpoint it makes absolutely sense to have abstract classes instead of interfaces. I will consider this change for the next major release 3.0.0. Thank you! |
yeaaaah |
finally... it took some years to align my synapses to that idea :-) |
I see the And there's the other side of the story: why would I have to accept to be denied to implement a contract? |
@nfekete @Kreinoee true, that's what the (polymorphic) open/closed principle is all about.
If an interface is not implemented as described by 'the contract' then it should be considered as a bug, not as a security hole. The same applies to Our collections are defined to be immutable/persistent. I also question if we should deny the possibility of extension. In an object-oriented world the are numerous, unforeseeable requirements to change the behavior of a type (e.g. proxying, ...). |
With this issue we will focus to make the shortcuts safe (see above |
But List is not the contract LinearSeq and Seq is. What does list add to the contract that these to interfaces is not? List is an concreate implementation of linearSeq just as Queue is it, and Queue is a concrete class that cannot be extended. Also comparing List from javaslang with List from java.util is wrong, as List is more on the same level as java.util.LinkedList, where Seq and java.util.List is more alike. Having interfaces defining the contract, and then concrete not extendable classes providing standard implementations that can guaranty either immutability or a certain performance behaviour is in my opinion really good practice. Then your can freely chose when implemting code, if your want to work on contract level, or instead needs the concrete implementations. My quess is that the javaslang collection library is inspired by the scala collection framework, and if we look there, its actually done in this way. There are interfaces providing the contract, allowing for extendability, but the concrete implementations does not. Another reason is that implementing a class, so it actually can be extended can complicate stuff a bit, so its often better to disallow concrete classes being extended, and instead define the functionality in an interface. We can look at the guidelines for implementing custom collections in java, they say to extend AbstractCollection, AbstractSet, AbstractMap and so on, instead of extending the conrete classes like ArrayList, even though this is possibly. I would guess that if they could make ArrayList and the similar classes final they would do so, as extending these has turned out to be problematic. However they cannot due to backward compability. And finnally when the collections is used as fields in a class that needs to be fully immutable, then List is not an option as long as its an interface as illustrated her (assuming Person is an Immutable class) :
However if I change the List to a Vector the class can guaranty immutability, as Vector is an concrete class that cannot be extended. I think that this both demonstrates a problem with List in it self, but also that it's confusing that there is such a fundamental difference on two classes, that is actually on the same level of the hierarchy, of the same collection library. |
@Kreinoee good discussion! you are absolutely right. Several Javaslang types need to be adjusted. These are our real interfaces (white background): These changes need to be performed:
Furthermore there are more types that should not be extended, i.e. they shouldn't be interfaces:
I'm not sure if these types should or should not be interfaces. In Scala they are traits:
Note: We still need the We need to be careful with internal (package-private/protected) abstract classes. These exists a bug the hinders us from using method references (see #1326 (comment), http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8141122, http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8068254) |
I must admit that I am not aware of the reasons for Future and Promise being traits in Scala, but my best guess would be, that they have no characteristics that forces them to be none extendable:
|
See these tweets: |
We already do this in Vavr 1.0. |
I'm not sure if this is worth the effort or if it makes completely sense. It is subject to think about before starting to implement it...
I think it might be better to add a clear specification to the API docs than introducing new types, blowing up the hierarchy and so on. Simplicity wins.
With #1801, we should instead update the API for the case the underlying collection is empty. Here for example:
empty.prependAll(list)
will return list if list is instanceof List.Some of our collections are final classes, like Queue:
But other collections are interfaces, like List:
The PROBLEMATIC line may be a security issue, if someone else implemented List and passed an instance to prependAll().
We are able to ensure that we return only own implementations by introducing a hidden, tagging type:
Note: We can't prevent users from implementing List. But we can prevent to return a user-implemented type when operating on user implementations. Therefore I call SealedList a 'semi-sealed' interface.
The text was updated successfully, but these errors were encountered: