4 ms·
> So [...] you discover that an ArrayList of Integer is not interchangeable with an ArrayList of Number because you can stick a Double in one of them and blow u
by lazulicurio 7y ago
> So [...] you discover that an ArrayList of Integer is not interchangeable with an ArrayList of Number because you can stick a Double in one of them and blow up everyone expecting only whole numbers.
Is this really an issue with Java's type system though? If you get an ArrayList<Number> and you assume that it contains only whole numbers, that's on you.
FWIW, my personal opinion is that erasure is better than templated generics for most use cases. Even when it comes to performance: a good JIT should be able to specialize generic code that end up on a hot path. Erasure does cause some issues with reflection and value types, but I think that it provides a better foundation to start from when designing a type system.
Then again, I am quite a fan of Rust's generics and dyn/impl {Trait}. Might be worth doing something similar where you have two separate generic systems: one for boxed values, and one for unboxed values.
- esrauch 7y ago> Is this really an issue with Java's type system though? If you get an ArrayList<Number> and you assume that it contains only whole numbers, that's on you. The point is that you have an List<Integer> are you allowed to call a function that takes an List<Number>? The answer is effectively "yes it should be allowed" if its not mutating the List (all Integers can be safely read as Numbers) but no if the List is mutable (you can't handle the fact that it might insert a non-Integer number into the Integer-only List).
- joshuamorton 7y agoThis is less LSP and more the co/contravariance of container types, which is its own special hell.
- LessDmesg 7y agoThe hell stems from inheritance, which is just syntactic sugar over composition. And a very unhealthy kind of sugar as that: problems with co- and contravariance, problems with copying, the need for redundant type declarations. And inheritance doesn't guarantee autofulfillment of the Liskov principle, either. One might even wonder why inheritance is needed at all in a language. Go and Rust are certainly fine without it.
- hinkley 7y agoOne of the humblest smart guys I worked with years ago would try to convince me over lunch that the Next Big Language would have syntactic sugar to make composition as easy as inheritance. I think in part because I had intimated that I was trying to create a language (narrator: he didn't. It went nowhere) There are a couple languages that have added something like this in the interim, but nobody seems to have made it an idiomatic thing. Which is a shame, because I still think I might like to work in the world he dreamed up. You get a little more composition in languages like NodeJS for the very pragmatic reason that inheritance, at least historically, was not seamless and hence had a high degree of friction.
- pjmlp 7y agoAh, just like VB did with COM....
- hinkley 7y agoWith the possible exception of a couple of convenience methods, you can model collections (independently) as a source and sink for elements and avoid having to rely on variance at all. But we treat them as a storehouse of shared state... which ought to be our first warning that things are about to go sideways. Lots of people get confused by variance. They shouldn’t, but insisting in what people should or shouldn’t do rather than working with how they are is a sure route to frustration.
- NovaX 7y agoIt isn't a problem if you design an API by following the rules (PECS). Use wildcards for method parameters when this is the desired behavior. It is not hard to do things right. https://howtodoinjava.com/java/generics/java-generics-what-is-pecs-producer-extends-consumer-super https://howtodoinjava.com/java/generics/java-generics-what-i...
- yarg 7y agovoid doThings(List<? extends Number> values){ ... } This is catered for.