8 ms·
It was unsound from the first version with respect to arrays. If B is a subtype of A, then Java allows you to pass as an argument (or return) an array of B whe
by derriz 10y ago
It was unsound from the first version with respect to arrays. If B is a subtype of A, then Java allows you to pass as an argument (or return) an array of B where an array of A is specified in the type signature.
To see why this is broken, consider a method that takes an array of Animal and sets the first element to be a Cat instance. Java's type system will allow you to call this method with an array of Dog which will blow up at runtime on the assignment to the array element.
I noticed this immediately when I first started using Java (version 1.0.4!) as I had just read the classic 1985 OO type systems paper by Cardelli. I didn't have an OO background and was surprised that a "modern" language like Java would have this flaw in its type system given that the problem was well known years before.
- MrBuddyCasino 10y agoIs this a defect or a tradeoff? Passing arrays of subtypes seems useful to me. I never experienced such an error, but I suspect this is quickly caught and shouldn't be a big problem. But then I don't know anything about type system theory.
- int_19h 10y agoIt is both. Passing collections of subtypes where a read-only collection of supertypes is expected by the receiving code is fine, because the receiving code can't do anything that would break type safety. But Java decided to only have a single array type, as a matter of expediency. But they also wanted covariance, because, as you point out, sometimes it's just convenient - more expediency. And so the trade-off was to enforce safety at runtime. It's still a defect in a sense that the design itself could be made sound, if there were a read-only array reference type in Java.
- marvy 10y agoRead-only is too strict. Can't reverse the array, or sort it.
- int_19h 10y agoSure you can - you just need to copy it. And I didn't mean to imply that array references should always be read-only. What I meant is that there should be a way to say "the referenced array cannot be mutated through this reference", like const* in C. 90% of the time, when you pass or return arrays, this is exactly what you want.
- Groxx 10y agoWithout generics (this is java 1, remember) it seems like a reasonable tradeoff. Otherwise[1] you can't have array-modifying polymorphic code at all - how would you write a "reverse this array" function, for instance? You'd need one for every kind of array you want to reverse. Or a "copy" function - even if you're given a properly-sized-and-typed array to put the values in, you can't make a function that'd work on anything except a single type. You could force every array-function-caller to cast their args to e.g. `Object[]`, but that seems like it'd require more work for even less safety. Though I'd love a read-only concept in Java, for read purposes :| [1]: I think. If I'm wrong, someone please correct me!
- RHSeeger 10y agoYou could define what an array accepts vs returns separately (input vs output). Admittedly, you're pretty much into generics (good ones, not what Java has) at that point. Edit: Could use a better word for "good" that sounds less dismissive, but I couldn't think of one. Java's generics are lacking in certain areas, unfortunately.
- MrBuddyCasino 10y agoGenerics in Java are pretty complicated already, so much so that very few developers fully understand them. I'm not sure the gain in complexity would be worth the added safety. I'd like to see examples in languages where this is possible, if there are any?
- deleted 10y ago[deleted]
- emmelaich 10y agoI think it is a fundamental. Just because D isa B it does not mean list(D) isa list(B).
- prodigal_erik 10y agoWhat we need is listContaining(Dog) isa listContaining(Animal) listAccepting(Animal) isa listAccepting(Dog)
- STRML 10y agoWhich is precisely the idea of covariant getters / contravariant setters in some sounder languages.
- Kreeg 10y agoIIRC, Scala has covariant/contravariant generics (apologies for the bad terminology, I'm not too familiar with the details)
- Groxx 10y agosupport for "<? extends X>" and "<? super X>" is supporting covariant / contravariant. invariant would be "<x>". basically. the same concept applies in a number of areas, with varying support (e.g. Java doesn't support contravariance in its method overloading: http://stackoverflow.com/a/2501513/16295 http://stackoverflow.com/a/2501513/16295 )
- plaguuuuuu 10y agoBoth defect and trade-off. I can definitely see value in being able to pass an array of Dogs and treat it as an array of Animals, but it means that the array of Animals is not really an array of animals (because certain properties of a real array of Animals actually cause the code to break) and as a result the language is a little dirtier for it. Like, that's pretty horrible to be honest. What you really want in that instance is to be able to easily and explicitly cast the Dog[] to Animal[] before calling the method. A better language would make this easy instead. I think Java's Vector type can do it, so it surprises me that they allow that sort of type weirdness with arrays..
- marvy 10y agoI think you're wrong about Vector
- quotemstr 10y ago> Is this a defect or a tradeoff? I'm curious what line of thinking could prompt this question. Not all trade-offs are good choices just because they're trade-offs! Maybe it has something to do with the recent spate of language design choices justified with "it was a trade-off", as if the words "trade-off" is an incantation that puts a technical decision above criticism.
- marvy 10y ago> Not all trade-offs are good choices just because they're trade-offs. Indeed. But they're not automatically bad choices either. Are you saying this one is a bad trade-off?
- nradov 10y agoThe existence of arrays at all is a trade off. The language would be more type safe and more consistent with OO best practices without arrays and using dynamic random access lists instead. But that would be too slow for some applications.
- deleted 10y ago[deleted]
- airplane 10y agoDon't think the scenario perfectly fits because before generics you couldn't have a method that takes in an Array of Animal, just an Array. Can a language could resolve this issue without generics or some other contract feature (which Java did later add), or without limiting the scope of array definitions and their use? May be good to note that Array<Dog> doesn't actually extend Array<Animal>, so there shouldn't be any polymorphic usage between them, and the return statement and method call scenarios are compile time errors with proper generics.
- quotemstr 10y ago> before generics you couldn't have a method that takes in an Array of Animal, just an Array. Arrays aren't generic types like ArrayList. Arrays are core language constructs parameterized by their element types. Array types are not erased in Java.
- kuschku 10y agoEhm, no? You can’t pass Dog[] where Animal[] is expected, it’s one of the more interesting properties of the Array, because its the only case where that doesn’t work. For the exact reason you mentioned.
- aioprisan 10y agoLooks like you missed the gist of the post, then. Code it up, you'll see it in action very clearly.
- AnimalMuppet 10y agoI believe you missed the GP's point. It's not that "you can't because the language stops you", it's that "you can't because it doesn't work". That is, you're agreeing, not disagreeing.
- pvg 10y agoTry it. http://ideone.com/hJa0yf http://ideone.com/hJa0yf
- Jean-Philipe 10y agoAnother fundamental flaw in my opinion is the cats-and-dogs analogy frequently used to highlight the use of inheritance in OO. Cats and dogs being mammals is a taxonomy. It's data, not code. Unfortunately, I see OO programmers coding taxonomies all the time just for the sake of it, ending up with deep inheritance trees of classes without ever taking the step back and asking themselves, why they do it the first place. (e.g. to share functionality or to reuse code)
- willtim 10y agoTaxonomies are common in the real world, so it is natural to try to model them. If the language lacks sum types (the mathematical dual of product types, a.k.a. tagged unions) then programmers are forced into using inheritance to encode it. So I blame the language, not the programmers.
- gregoor 10y agoI understood JP's critique to be more about mixing behaviour with data than about alternative ways of modelling inheritance.
- willtim 10y agoBut Java strongly encourages the mixing of data and behaviour. There is only virtual method dispatch and switch on ints/enums for efficient case analysis. We therefore need complex encodings like the Visitor pattern, if we are to avoid conflating data and behaviour. So again, I blame the language not the programmers.
- dvt 10y agoThis, imo, is actually a bigger deal than generics. Since type erasure does all kinds of "magic", it doesn't surprise me that there will end up some kind of (T)-type casts somewhere in the OP's bytecode. But the fact that the following is unsound (and has been for a while) will always remain a mystery to me: public class Main { class Animal {} class Dog extends Animal {} class Cat extends Animal {} public static void main (String[] args) { Main x = new Main(); Dog[] doges = new Dog[10]; x.t(doges); } public void t(Animal[] bar) { bar[0] = new Cat(); bar[0] = new Animal(); // also breaks } } The array type seems completely tractable and may be able to be caught by the compiler.
- trendkill 10y agoA joke from my childhood: Man goes to doctor, and says: "Gee, doc, it really hurts when I do this." Doctor replies: "Then don't do that."
- paulddraper 10y agoTwo men stand on a bridge. The first leans on the railing, which gives way and sends him over the edge. Horrified, the second looks around and spots a civil engineer. Engineer shrugs and says, "Don't do that."
- JoshTriplett 10y agoThis would be fine if t's array bar only allowed mutation of the objects within the array, but didn't allow mutation of the array itself. Treating a Dog as an Animal works fine; however, you can only treat a Dog[] as a const Animal[], not a mutable Animal[].
- vbezhenar 10y agoYep, it's a huge hole. Arrays in Java should be Object[] or primitive[], not Animal[]. That's one of the reasons that object arrays should be avoided (something like ArrayList should be used instead).