6 ms·
Are there any java programmers around who are surprised by this? The docs don't beat around the bush: They state rather clearly that [A] generics can be used to
by rzwitserloot 5y ago
Are there any java programmers around who are surprised by this? The docs don't beat around the bush: They state rather clearly that [A] generics can be used to cause heap corruption and [B] `null` is all types and no types at the same time (it can be cast to anything, it is `instanceof` nothing).
Any thought put into what that might mean for the soundness of the type system trivially leads to the 'correct' conclusions.
I'm not sure the headline is 'deserved' (it insinuates more alarm than I think is warranted), and the actual paper linked __definitely__ is engaging in some unwarranted hyperbole. "The existential crisis of null"?
- watwut 5y agoI interpreted "The existential crisis of null" as funny catchy title for conference talk or some such. I am Java programmer, actually like java and took no offense on it.
- Banana699 5y ago>actually like java I honestly can't fathom how that works, I thought aversion to verbosity is near-universal among programmers, so how can somebody _like_ (not just use or be okay with) a language that requires you to state the maddeningly obvious and do a mountain of paperwork all the time, so many times and for the simplest most trivial tasks. It's the modern day COBOL and worse. When I first saw Java coming from C++ and Python object models (among others, but those two I'm most familiar with), I felt the same rage upon seeing that the accessibility modifier must be put before every field or method as people feel when they have to jump through stupid bureaucratic hoops. C++ just requires you to put "['public'|'protected'|'private'] :" then every field or method coming after that colon will be of the accessibility before it, which is the commonsense thing to do because usually all publics are together and all privates are together. It's something so very trivial and so utterly lowbar that C++ managed to do it right on the first try, I don't know what frame of mind was playing out in the mind of the first person who thought that this common thing needs to be multiplied out and stated for every field and every method it annotates. This was the archtypical model of my interaction with Java ever since, everytime I hear the name I instantly think of the stupid and meaningless convoluted procedures typical of corrupt governments and bloated administration systems. "veryLongTypeName veryLongObjectName = new veryLongTypeName" everywhere as far as the eye can see, obscuring the dance of algorithms and datastructures expressing the core logic in favor of trivialities about allocations and declarations, then designers introduce the useless syntax sugar "var" and gloat about how that enhances readability in some cases, as if the language's syntax shouldn't have been designed like so and trimmed much much more than that from the very beginning. "Everything is a class" because we want to be so cool like our idol objective-C's idol smalltalk, so you have to do stupid things like naming a class "fooUtils" to signal it's just a namespace of static methods, but actually forget that not everything is a class and generics can't be used with primitves because reasons, nevermind the extremly obvious and painfully trivial solution of having the compiler generate a generic_primitiveType class for every generic it encounters, even if it's still a quick-and-dirty hack and only works with code with source available it would be infinitely better and more clean than the hack job they did with autoboxing and unboxing. Etc, etc, the language is so comically badly-designed it's baffling, I don't think they could have done a worse job if they tried.
- dzaima 5y ago'public:' / 'private:' / whatever may save you a couple milliseconds when defining a new class, sure. At the cost of having to scan arbitrarily far backwards to find the visibility of a given thing. I personally often don't group things by public/private, but by their utility (e.g. blocks of fields by what part of state they store, regardless of what of that state is public, or putting private fields caching values near the public function that sets/reads them). I like being able to immediately look at a line, and know what type of variable it declares, and, separately, look at what the variable definition does. When reading code top-to-bottom, you can pretty much ignore variable types, but when scanning code for what you want, you pretty much ignore code, and look only at variable types. Having to wrap your static utilities in a class does seem a bit pointless, but the alternatives are having a whole another system for outside-of-any-class static methods (which complicates the language model and makes moving parts of your program around harder), or having 'namespace YourUtils { … }', which isn't all that different from a class anyways. Generics are by far the thing I like least about Java. And that's represented by how they were added to Java - bolted on, not actually properly designed in. A problem with autogenerated types is that that'd generate a new type for each compiled jar file, and including jar files would then have the same type defined multiple times. As far as I understand, being able to trivially fall back to non-generic types was also a reason for the weirdness. (yes, these are horrible reasons, and have resulted in a vastly worse language. Unfortunately the designers valued keeping old things working over making future things better. Yes, Java could easily be better. But it still manages to be good enough for people.)
- Banana699 5y ago>'public:' / 'private:' / whatever may save you a couple milliseconds when defining a new class The issue is not at all the time you save at write-time, this is completely unimportant and automated by [IDEs|text editor macros] anyway, the issue is reading the code later. Imagine that every time I write a sentence I prepend "Banana699 says" or "Banana699 thinks", this could be automated enough to be non-bothering to me as I write it, but imagine the sheer annoying line noise that will be my paragraphs with this. This is also bad for me (the writer) because I read and re-read my words when I write, having it full of noise clutters my thought process and impedes thinking about important things. > I personally often don't group things by public/private, but by their utility (e.g. blocks of fields by what part of state they store) You can still do this in C++, the class declaration can contain any sequence of 'modifier: declarations" clauses. (At the very extreme end, you can simply prepend 'modifier:' to every field and method and just have your java syntax back with an additional ':', the fragment "public: int x; private: int y; public: int z;" is valid C++ inside a class or struct declaration and does exactly what you'd think it would. But this is obviousely non-optimal in this case and so many others.) >you can pretty much ignore variable types Just like you can ignore my irrelevant username if I put it before every sentence I write, but the mere presence of irrelevant info steals your attention and clutters the code for no good reason. >look only at variable types Which you can still get and very easily at that in an inferred-types language if you're using any moderately modern IDE/TextEditor. Haskell is one language whose type inference can feel like magic sometimes, and yet a simple hovering over the identifier in even a lightweight IDE like repl.it reveals what you want. It's not a problem at all. Not all info has to be stored in the raw text, after all even Java is not bad enough to require you to use "new foo.bar.baz.class" even though it would be "clearer" in the strict sense of the word. Type inference simply applies a step further, and you can completely ignore it if you want. >Having to wrap your static utilities in a class does seem a bit pointless Meh, it's not a hill I'd want to die on, just a single example that I could have ignored if it was isolated. >generate a new type for each compiled jar file, and including jar files would then have the same type defined multiple times This is a bit confusing, I will rephrase it so I can be sure what I'm replying to, you can correct me if I misunderstood you. You're highlighting the problem that if library B and library C both had a common ancestor library A, and they both use the generic type Ta<int>, then the compiler (or possibly two different compilers) generates Ta_int_Autogen two times for each of them because they are totally isolated. But if a library D includes both of them, then that would mean including two versions and that would cause bad performance and possibly other funny things. That's C++ template problem, it can be completely avoided in Java's object model because there is (currently) a finite (and very small) number of types that you need to autogenerate. The compiler can simply treat "class generic<A> {...}" as syntax sugar for the following declarations "class generic_for_object {...} class generic_for_int {...} [... and so on for the rest of primitve types]". This is done at declaration time, i.e. in library A itself. All downstream code would then use this one version and this one version only, there is nothing else. The reason C++ can't do this is that the semantics of C++ generics is literally and explicitely "Yes, this is just copy-paste for every type you instantiate the generic on". The compiler has no other choice but to instantiate on a use-by-use basis. If it started eagerly auto-generating at declaration time, it would never run out of types to auto-generate for. (it can't even know all the types the generic could be instantiated on, neither a common ancestor for all of them.) >As far as I understand, being able to trivially fall back to non-generic types was also a reason for the weirdness I don't actually understand what that means. What's the fallback and who's doing it? when and why ? >But it still manages to be good enough for people Anything can be good enough if you're constrained enough, what I found surprising is the "liking" part. For me, liking a language means that, in absence of all other constraints and externalities (back-compat, hype and who-else-is-using-it syndrome, perf considerations,...), you would prefer that language over any other. I find it alien that somebody can think that of Java, the language is completely out-of-sync with every frequency in my brain, it's like trying to sing while a rabid dog is barking in your ear.
- dzaima 5y agoUsing casts between types with different generics can cause "heap corruption", but the compiler will give warnings on them (except if explicitly suppressed). The article doesn't use any such casts, but can still "cast" any non-null object of any type to any other, without any warnings emitted. The behavior used to get that to happen, as far as I remember, uses the java spec giving a concrete way on how to parse generics. Multiple other compilers (including the official Java 9 one, if I'm reading correctly) actually emitted an error on it, despite "technically" it being "valid" Java code, to emphasize how weird the situation is. edit: this is of similar (mostly academic) significance to, say, finding a case where you can double-free in Rust without any unsafe code. Sure, rust is unsafe if you use "unsafe", but other than that, you should hope it's sound. It's not at all as catastrophic as JVMs are already pretty much just dynamically typed though, but it's still a bad thing to have.
- 3836293648 5y agoMore accurate comparison: A double drop in rust. A double free would generally be a library bug
- pjmlp 5y agoNope, but some papers are required to state the obvious I guess.
- deleted 5y ago[deleted]
- Cthulhu_ 5y agoI mean. I don't believe Java or Scala claim type correctness or whatever the term is, compared to more formal languages.
- titzer 5y agoNo, you can't cause "heap corruption". One of the casts inserted by the javac compiler can fail at runtime though. The JVM does not know or care about generics; they are "erased."
- rzwitserloot 5y agoHaving a Dog in a list of Cats is called 'heap corruption' in most places (including the relevant mailing lists on openjdk itself). It doesn't cause core dumps or security issues, it merely means any attempt to meaningfully interact with it will cause a ClassCastException on use. Still called 'heap corruption'. Even if as far as the JVM is concerned, that's just a list of whatevers - it doesn't matter, on-use the bytecode will cast.