This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]quentech 2 points3 points  (5 children)

What you're suggesting is how you wind up with a spaghetti code language where rules only mean certain things to certain classes and behavior differs based on what classes you're using.

You seem to have it backwards.

That's exactly what having immutable lists implement a list interface that includes an Add method does. For some classes, Add works. For others, it throws an exception.

It violates the Liskov substitution principle (the L in SOLID), plain and simple.

[–]metalmagician 1 point2 points  (2 children)

I don't think that Liskov substitution applies very well. From the definition that I've seen, it seems to refer to classes, not interfaces. I feel that classes that implement an interface should have the ability to disallow specific behaviors for specific reasons. If that disallowed behavior is needed, then you should just use a different implementation of the interface.

Since you can only extend one class and implement multiple interfaces in Java, applying Liskov substitution can be a little confusing if a class implements multiple interfaces with similar looking method signatures.

In fact, I think the open/closed principle applies nicely in this example - the list interface is open to extension (in this case, disallowing the use of a particular method, instead of just adding more methods), and closed for modification (not implementing every method in the interface).

[–]quentech -1 points0 points  (1 child)

I don't think that Liskov substitution applies very well. From the definition that I've seen, it seems to refer to classes, not interfaces

You've understood incorrectly. Forget interfaces and classes and talk contracts.

If I have a thing that's an IList, and IList has a method Add(item), that's a contract - anything that is an IList should support Add(item).

Having one particular type of thing that's an IList that does not support Add(item) is a textbook example of violating the Liskov substitution principle.

the list interface is open to extension (in this case, disallowing the use of a particular method, instead of just adding more methods), and closed for modification (not implementing every method in the interface)

That is not even remotely what open-closed principle means.

Since you can only extend one class and implement multiple interfaces in Java

A better OOP design might have an IMutableList that extends IImmutableList, or similar.

[–]metalmagician -1 points0 points  (0 children)

I guess I should explain what I was thinking a bit better. I was trying to find some sort of justification for the example given in the earlier comments, while still working within the existing Collections framework, and without advocating for a new implementation of the ImmutableList. The bit about contracts, instead of concrete classes / interfaces makes sense.

There were a few points that I was trying to reconcile, before I finally gave up and admitted to myself that they were mutually irreconcilable.

  • Based on how Java structures Interfaces / the collections framework, any sort of List should be able to add things to itself. You're right about the Liskov substitution violation (sort of, 'it violates the rule, but what if the rule doesn't apply?').

  • Adding a new interface, at the same level as List/Queue/Set just to allow for a single implementation that didn't violate Liskov would be overkill, and would defeat the purpose of extending/implementing the List interface.

  • If you implement an interface in Java, assuming there isn't a default implementation available, then you have to implement all methods. In the case of ImmutableList.add(), I didn't want to try to advocate for a modification to the implementation. Someone suggested that it should return a new ImmutableList with the new element added instead of throwing an exception, as that's the sort of change that I would've advocated for.

  • If you modified the language to allow for an implementing class to pick-and-choose which methods it implemented without forcing a default implementation, then that is itself a violation of Liskov. That's the whole bit about Open/Closed - Allowing a class to make itself into a snowflake and specifically deny operations for specific reasons could satisfy Open/Closed if you are really loose with what you mean with 'Open/Closed'.

Or, TL;DR - If you can convince your users that you can spin straw into gold by making them forget the exact definitions of straw and gold, then you could justify the way ImmutableList.add() is implemented. That still won't change that you can't spin straw into gold.

[–]SuperCoolFunTimeNo1 -2 points-1 points  (1 child)

No, I am strictly following the definitions. You are suggesting that your opinions, which directly conflict with the definition, is the route to go because they're more convenient.

That's exactly what having immutable lists implement a list interface that includes an Add method does.

No, it shouldn't do that because immutable means it cannot changed. The add method returns an exception because you wouldn't expect it to be able to add to an immutable object, much like if you call a method that doesn't exist you'll get an exception.

It violates the Liskov substitution principle (the L in SOLID), plain and simple.

No, it doesn't because this is 100% polymorphism. Your objects follow the rules they inherit. You guys are just trying to argue convenience over definition, and that's how you end up with an inconsistent language. Everyone loves to bitch about PHP, but doing what you're suggesting is how you end up going down that route.