6 ms·
Yes, seems like Java generics are an argument against the "generics allow optimization" argument. Maybe I misunderstood what he was trying to say.
by zwieback 4y ago
Yes, seems like Java generics are an argument against the "generics allow optimization" argument. Maybe I misunderstood what he was trying to say.
- lifthrasiir 4y agoGenerics allow optimization, but that optimization has to be implemented in the first place. Java generics is an afterthought so you can expect such a suboptimal design.
- malfist 4y agoFeatures added after launch doesn't make something an afterthought. Java generics are a key part of the language.
- switchbak 4y agoGenerics were only added in Java 1.5, they were absolutely not part of the original design, and type erasure is very much a considerable restriction in some cases. I would wager that a greenfield design would look quite different. Whether you call that an afterthought? I'm sure the designers had this in mind, but at the same time they painted themselves into a corner somewhat with earlier decisions and a huge dedication to backward compatibility. Their work on threads on the other hand shows some real forethought and they were able to avoid most of the pitfalls that a few other language impl's fell into.
- wvenable 4y agoGenerics in C# were also added after launch but have a significantly better implementation. While they are a key part of the language they aren't a key part of the platform.
- cogman10 4y agoJava optimizes at the method level and the majority of methods when writing an app will have constant types used against them. Your method signature may look like void foo(Iterable<Bar> b) which would make you think of vtables and casting performance losses. However, Java's JIT is fairly cleaver. It can tell that you always call `foo` with an `ArrayList` and it can tell that `ArrayList`'s iterator always emits a `Bar` Object. From there it can elate a lot of the type tables and virtual dispatch to just be direct dispatch. It does that at the cost of a couple of added runtime checks to make sure those assumption's aren't violated. The impact of generics on performance has, for me, generally been pretty low. I've seen a much higher impact due to java's memory model (which is getting fixed with Valhalla). This, btw, is how JavaScript's and indeed most high speed JITs function.
- lifthrasiir 4y agoYeah, JIT does subsume naive AOT + generics for many cases, but it is a bit unfair comparison because there is no reason for other languages to implement such optimizations (PGO is one of them). And such an inference is not free and causes usual JIT pain points.
- kaba0 4y agoI don’t think it is fair to call it an afterthought. I would even say that it was extremely well thought out, managing to move a whole ecosystem with millions of lines of code from unsafe raw usage to type-safe generics without much hiccup, at a time when this feature wasn’t all that well known by the average developer.
- titzer 4y agoErasure was a bad design choice. C# did the exact same upgrade (add generics to the language), but they chose to reify them and add them to the binary format, with a better overall result (IMHO). C# does not have platform reach that Java has, but there are a lot of factors at play there that go beyond language design choices.
- kaba0 4y agoAs I learned from another commenter here, C# 1.0 didn’t actually have generics at a language level, but was already designed that way on a vm level. So C# 2.0 could take advantage of that, making it much more of a planned feature from the start. Also, I don’t see erased generics all that big of a deal — if there was no reflection (which is not that common feature in languages) one could not even differentiate between the two models. Haskell also does complete type erasure for example. As for another view point, it is often brought up on HN that what made the JVM language ecosystem so thriving is exactly the type erasure model. C# effectively forces their idea of generics onto the whole platform, while JVM languages are free to choose their own model. I don’t know how true is that, but empirically it does seem to sound true (F# does share its generics model with C#, and the CLR has a history of getting some attempts at being a target which is later dropped. Though I assume it is more often due to it simply being a smaller target)
- titzer 4y ago> if there was no reflection (which is not that common feature in languages) one could not even differentiate between the two models. Haskell also does complete type erasure for example. You can, even without reflection, because the Java generic type system doesn't have the parametricity property; you can detect runtime types with cast/instanceof. Haskell doesn't have non-parametric operations, so erasure is not observable.
- marginalia_nu 4y agoThe path Java chooses for optimization is through JIT, but that's entirely orthogonal to generics. Java generics are a syntactic construction, there really is no JVM support for them what so ever.
- cogman10 4y agoIt's complicated. There actually IS JVM support for generics in surprising locations. Just not at method dispatch... sort of. For example void <T extends Foo> bar(T f); will, under the covers, create a function that looks like void bar(Foo f); If you create a class class Foo implements Supplier<Bar> { Bar get() { return new Bar(); } } The method signature for `Foo` does in fact return a `Bar` type and not some `Object` type even though `Supplier<Bar>` would imply that is erased. The same applies to if that generic was used within the method parameters. That generic type information is, in fact, stored off at the class level and can be extracted through Java's reflection API. This is how magic like json.parse("[1,2,3]", new TypeReference<List<Integer>>{}); is getting it's stuff done. It's creating a new anonymous class of "TypeReference<List<Integer>>" which the json library can then use to introspect and pull the generic information out and know that it should create a `List` of `Integer` rather than of `Long` or `Double` or a `Set`. This makes generics more than just a syntactic construction in Java.
- marginalia_nu 4y agoclass Foo implements Supplier<Bar> { Bar get() { return new Bar(); } } Bar isn't a generic in Foo though (if we remove the implements-clause, that is very clear). It's only a generic in Supplier, so I don't see where the implied erasure quite comes from.
- cogman10 4y agoI didn't add it, but the `get` method comes is an override from the `Supplier`. For example, if we did something like class Foo extends HashMap<Foo, Bar> { @Override Bar put(Foo f, Bar b) { super.put(f, b); } } The `Map` signature for `put` is `Object put(Object, Object)` due to erasures. However, because you are extending and specifying the generics, the overriden method ends up reifying the type. All this is to say that generics end up being a little more than just syntactic sugar.
- mumblemumble 4y agoI think the author got it slightly wrong here because he's conflating the programming language feature and its semantics with the run-time implications of the technique(s) you use to implement that feature. For what it's worth, this seems to be a common misunderstanding where generics are concerned.
- kaba0 4y agoThat is also not true — Java’s generics don’t add any overhead. If you would have written an ArrayList implementation before generics you would have had the exact same compiled method signature like put(Object o) and Object get() - the only difference is that it would not have been type safe, that is you could have inserted a String into it while you might have expected to get an Integer on the other side. So generics are completely a language semantic concept in case of Java, with no performance detriment/benefit. Of course more information can be used up for better optimizations as well, see Rust’s or C++’s generics/templates.
- titzer 4y agoThe javac source compiler erases source-level types and inserts casts into the bytecode (at usage sites, not within the generic class). So if you have ArrayList<T>, it will generate one version of ArrayList. But usage sites will have casts inserted where necessary. In particular, in a use of put() on the outside doesn't need a cast (because the single implementation of ArrayList uses Object underneath), but get() will have a cast from Object to String at the call site.