[HN Gopher] Background: how we got the generics we have (2020)
       ___________________________________________________________________
        
       Background: how we got the generics we have (2020)
        
       Author : kasperni
       Score  : 55 points
       Date   : 2021-04-01 09:23 UTC (13 hours ago)
        
 (HTM) web link (cr.openjdk.java.net)
 (TXT) w3m dump (cr.openjdk.java.net)
        
       | haolez wrote:
       | Kind of off topic, but is something using either Lombok[0] or
       | Manifold[1] to have better ergonomics in Java? It sounds like a
       | brilliant idea to me, but I don't use Java on a daily basis.
       | 
       | [0] https://projectlombok.org/ [1] https://github.com/manifold-
       | systems/manifold
        
         | virmundi wrote:
         | The problem I have with Lombok is that suddenly it's hard to
         | trace getters and setter from the Pojo. By this I mean asking
         | the question, "where is getId() used" is hard if I want to use
         | the find references feature of the IDE.
        
           | v-erne wrote:
           | I'm not sure how this works in other IDEs, but lombock
           | Eclipse plugin kinda solves this problem. You can run
           | CTRL+Shift+G (Find references) on getter/setter that is shown
           | in class outline and it will immediately give You
           | getter/setter uses.
           | 
           | https://stackoverflow.com/questions/42644923/eclipse-with-
           | lo...
        
           | LgWoodenBadger wrote:
           | Depends on your IDE. IntelliJ IDEA supports this with the
           | Lombok plugin
        
           | lmilcin wrote:
           | I also have mixed feelings about Lombok.
           | 
           | For example, if you want to automatically create getters and
           | setters to all private variables, why not make them public in
           | the first place?
           | 
           | I hate when tools mess with IDE. What about guys using Emacs
           | (or any other editor that does not understand Lombok)? I
           | think generating code from IDE is fine as well as having code
           | generated from build system distributed along with code, but
           | having that code generated through magic of annotation
           | processor is not as nice. I also suspect that Lombok messes
           | my IDE on a deeper level but I have not yet a good concrete
           | proof. Recently I am more and more frequently finding
           | situations where Idea messes up understanding correct types
           | of things and it takes for it some time to catch up (and
           | sometimes reloading the file).
           | 
           | On the other hand I love small things like creating builder
           | or logger. Which is to say these can be just as easily
           | generated from a template, no annotation processor needs to
           | be involved.
           | 
           | I think main feature of Java for many years were its IDEs
           | which would allow to run accurate refactorings or always
           | understand who is using particular piece of code, what is
           | exact call hierarchy, etc. I feel loosing that is going to
           | make Java much less useful for me when I try to analyze large
           | project or run huge refactorings.
        
             | p2t2p wrote:
             | > why not make them public in the first place?
             | 
             | Because in future I may want to add some validation there,
             | or processing, like sending events on set of a value, etc.
             | 
             | Properties in C# was a genius move that solves it
             | correctly.
        
               | watwut wrote:
               | Unless we are talking about API, then you add getter and
               | setter. You might even be able to refactor all callers
               | automatically via IDE. I mean, I dont mind getters and
               | setters in java, it is standard at this point.
               | 
               | But origin of it is in EJB specification, it is not
               | something that would be interently needed elsewhere. It
               | just "feels wrong" to programmers (including me) at this
               | point.
        
               | lmilcin wrote:
               | That, in my observation, almost never happens.
        
               | p2t2p wrote:
               | I'm working in a product that is about 10 years old and 4
               | months ago it went through a quite a big project which
               | purpose was to add extended auditing to be more
               | enterprise friendly. The system was flooded with auditing
               | events on various changes into internal data. The system
               | provides a public API and allows to install plugins. If
               | we had to change our entities that would be a
               | compatibility nightmare for thousands of plugin vendors.
        
             | goostavos wrote:
             | >why not make them public in the first place?
             | 
             | If you're in an old school "OOP" style java codebase, where
             | each class is its own little island of encapsulated state
             | and behavior, the coupling of the two means that strictly
             | controlling access via getters is usually required.
             | 
             | Even in more "modern" java, where you do most of your
             | programing against plain data, having Lombok attach getters
             | still gives you some quality of Life benefits because you
             | can use method reference rather than anonymous functions
             | for high-order access (e.g. `things.map(MyClass::getName)`
             | rather than `things.map(myclass -> myclass.name)`)
             | 
             | I'm with you on mixed Lombok feelings, though. It's great
             | right up until its not. It fails or doesn't work in really
             | unexpected or weird ways, and is the source endless pain
             | when you've got other annotation based things (like dagger)
             | which then imposes annotation processing order failures.
        
               | lmilcin wrote:
               | > If you're in an old school "OOP" style java codebase,
               | where each class is its own little island of encapsulated
               | state and behavior, the coupling of the two means that
               | strictly controlling access via getters is usually
               | required.
               | 
               | That I agree. My comment was meant to be cheeky a little
               | bit as it is actually pretty difficult to find trully OOP
               | application, at least in area of backend apps I am
               | working in.
               | 
               | Obviously, in OOP you are not supposed to expose your
               | internal state but rather accept messages to run
               | behaviour.
               | 
               | If you are working on a truly OOP application then Lombok
               | is useless. Your public class interface is all that
               | matters then and you would spend more time fighting
               | Lombok than just writing it manually.
               | 
               | Lombok is only good at automating boilerplate which, if
               | you have a lot of, is a sign of some other problem.
               | 
               | > It's great right up until its not. It fails or doesn't
               | work in really unexpected or weird ways
               | 
               | That is exactly the point. Magic is fun until you find
               | out that it leaks horribly and causes unintended side
               | effects with all sorts of stuff.
               | 
               | What's the point of replacing simple problem (just really
               | use templating mechanism you have in your IDE) with
               | complex problem (dealing with magic failing on you).
        
         | windock wrote:
         | Lombok is very widely used in places I worked. Love it myself.
        
           | [deleted]
        
         | watwut wrote:
         | We use it, but I don't like it. I does superficial stuff for
         | me, but also makes navigating and reading code harder. It takes
         | somewhat more time to figure out all setter callers and stuff
         | like that.
         | 
         | It is one of those things that does not much useful, but takes
         | away only little bit so there is no reason to fight it too
         | much.
        
         | dehrmann wrote:
         | At least with Lombok, I've found it makes builds and tooling
         | flaky, so I'd rather write an extra line here and there (or use
         | Kotlin).
        
         | setheron wrote:
         | Never heard of manifold. Thanks for sharing.
        
       | gkorland wrote:
       | I think the way Rust took with Generics should be the next step
       | for Java, it for sure can boost the Java apps performance. In
       | Rust you have both types erasure and C++ style templates which
       | don't erasure the types.
        
         | mrkeen wrote:
         | > the way Rust took with Generics should be the next step for
         | Java
         | 
         | I have a List<Integer>. Maybe I want to replace List with
         | something else, call it L.
         | 
         | Can I write L<Integer> in Java? No. Rust? No. C#? No Kotlin?
         | No.
         | 
         | Does Go have generics yet? Once it does, I'm sure it'll be a No
         | as well.
         | 
         | Haskell and Scala got it right.
        
           | riffraff wrote:
           | I don't understand what you're saying, what is the problem
           | with L<Integer> ?
        
             | Taig wrote:
             | The feature that mrkeen is describing is called higher
             | kinded types (HKT). I.e. in Scala (and similarly in
             | Haskell), you could write something like:
             | def process[L[_]](input: L[Int]): Unit = ???
             | 
             | Where `L[_]` could be any type that has a type parameter,
             | e.g. `List[_]`, `Option[_]`, `Future[_]` or `Either[String,
             | *]`. This opens the door to a new level of abstraction
             | where other languages must resort to code generation or
             | similar ad hoc-like solutions.
        
             | [deleted]
        
             | mrkeen wrote:
             | There shouldn't be a problem with it!                   <L>
             | void process(final L<Integer> input) {         }
             | ------------------------------------------              <L>
             | void process(final L<Integer> input) {
             | ^         required: class         found:    type parameter
             | L         where L is a type-variable:           L extends
             | Object declared in method <L>process(<any>)
        
               | p2t2p wrote:
               | Works perfectly:
               | 
               | public <T extends Collection<Integer>> T process(T
               | collectionOfInteger) { return collectionOfInteger; }
        
               | petersellers wrote:
               | What if your "T" isn't a collection though?
        
               | Joker_vD wrote:
               | Then what can you really do with it except of returning
               | it back as-is?
        
               | willtim wrote:
               | This uses subtyping polymorphism not higher-kinded
               | parametric polymorphism. If subtyping worked perfectly in
               | all important cases, "generics" (parametric polymorphism)
               | would not have been retrofitted to Java.
        
           | fooker wrote:
           | Can do with C++ though. https://en.cppreference.com/w/cpp/lan
           | guage/template_paramete...
        
           | josefx wrote:
           | What operations can you perform on an L<Integer>? Does it
           | work based on duck typing instead of explicitly declaring
           | what base interface (Collection<Integer>) it requires?
        
             | dboreham wrote:
             | It's a similar idea to inheriting from a base collection
             | class (iterable, etc). Not quite the same though. It is a
             | kind of duck typing I suppose. You get to specify the
             | nature of a class of types. e.g. it can be ordered, behaves
             | like a number, etc.
        
             | thinkharderdev wrote:
             | Generally you are using a typeclass to specify behavior
             | (which I guess is a kind of duck-typing).
             | 
             | For example, there is a typeclass Foldable[F[_]] which
             | provides the ability to "fold" (i.e. recurse over) some
             | type F[_] which takes a type parameter. Then you have
             | typeclass definitions for Foldable[Set], Foldable[List],
             | Foldable[Tree], Foldable[Option], etc.
             | 
             | Then if you have an operation and you want it to work on
             | any collection that can be folded, then you can just define
             | it in terms of a generic type that has a Foldable
             | typeclass. For instance to generically sum a collection of
             | ints, you might do
             | 
             | def sum[F[_] : Foldable](ints: F[Int]): Int = ...
             | 
             | Now you can call sum(List(1,2,3)), sum(Set(1,2,3)),
             | sum(Option(1)), etc...
        
               | Joker_vD wrote:
               | Typeclass instance = Adaptor/Decorator in OOP terms,
               | automatically derived (and wrapped in) for you by the
               | compiler.
               | 
               | You want to fold over Options? Make a FoldableOption
               | class implementing Foldable<Option> and pass "new
               | FoldableOption(myOptionValue)" to whatever places you
               | need. Haskell does pretty much the same (unless they
               | moved away from dictionary passing).
        
           | p2t2p wrote:
           | No worries in Java (and I bet in C#) in works no problemo:
           | 
           | public <T extends Collection<Integer>> T process(T
           | collectionOfInteger) { return collectionOfInteger; }
           | 
           | And basically your T will be as wide as the interface/class
           | it extends in the generic parameter.
           | 
           | I guess the most Java way to do that would be using Iterable:
           | 
           | public <Z, T extends Iterable<Z>> T process(T iterable) {
           | return iterable; }
        
             | vimax wrote:
             | I want to do something like this:
             | 
             | public <Z, T extends Iterable<Z>, R> T<R> process(T,
             | Function<Z,R>)
             | 
             | change the type parameter of T<Z> to T<R> and return an
             | instance of T<R>, but the best I can do is return an
             | instance of Iterable<R> because I cannot define T to have a
             | type parameter.
        
               | mrkeen wrote:
               | This comment explains my problem much better than I did.
        
             | p2t2p wrote:
             | I can turn any collection to list like that:
             | 
             | public <Z, T extends Collection<Z>> List<Z> process(T
             | collectionOfInteger) { return collectionOfInteger.stream()
             | .collect(toList()); }
        
               | mrkeen wrote:
               | Great!
               | 
               | These are the three implementations I want to hide behind
               | a single abstraction:                   //
               | java.util.Optional         public <U> Optional<U>
               | flatMap(Function<? super T, ? extends Optional<? extends
               | U>> mapper)              //
               | java.util.concurrent.CompletionStage:         public <U>
               | CompletionStage<U> thenCompose(Function<? super T, ?
               | extends CompletionStage<U>> fn);              //
               | java.util.stream.Stream:         <R> Stream<R>
               | flatMap(Function<? super T, ? extends Stream<? extends
               | R>> mapper);
               | 
               | Any suggestions?
        
               | p2t2p wrote:
               | Also I don't understand why would you need HKT here. I
               | don't see language limitation here, I see poorly designed
               | inheritance in the standard library. They should've
               | introduced "FlatMappable" type and make Option and Stream
               | extend it. CompletionStage, I'm torn on it, on one hand
               | yes, it is a flatMap essentially but semantics is a bit
               | different...
        
               | p2t2p wrote:
               | Oh my god, there's more to that, Optional is a class, not
               | an interface and it's final :facepalm:
        
               | mrkeen wrote:
               | I've made my peace with those aspects of the libraries.
               | I'm happy to wrap the standard classes in MyOption,
               | MyStream, etc. Then I can extend FlatMappable myself. I
               | just don't know how to declare and implement FlatMappable
               | in a sane way (preserving type information, not needing
               | casts, etc.)
        
               | Joker_vD wrote:
               | Sure:                   public interface FlatMappable<T>
               | {             <U> Optional<U> flatMap(Function<? super T,
               | ? extends Optional<? extends U>> mapper);         }
               | public class FlatMappableOption<T> implements
               | FlatMappable<T> {             private final Option<T>
               | value;                  public
               | FlatMappableOption<T>(Option<T> value) {
               | this.value = value;             }                  <U>
               | Optional<U> flatMap(Function<? super T, ? extends
               | Optional<? extends U>> mapper) {                 return
               | value.flatMap(mapper);             }         }
               | // similar wrappers for CompletionStage and Stream
               | 
               | Now whenever you want to pass a
               | Stream/CompletionStage/Option to something that accepts
               | FlatMappable, you either a) already has it wrapped in
               | FlatMappable, so pass that wrapper in; or b) has a naked
               | value of known type, so wrap it in the propper wrapper
               | class and pass the resulting wrapper in.
               | 
               | Of course, it would be nice to have this wrapping done
               | automatically instead of manually, but that's pretty much
               | how it's actually implemented behind the scenes
               | (dictionary passing, or look at Go's way to wrap things
               | inside interface{}).
        
               | p2t2p wrote:
               | I'm not sure if I catch your drift, but if I do:
               | public <T, Z, F extends Function<? super T, Z>> Z
               | apply(Function<F, Z> target, F fn) {             return
               | target.apply(fn);         }              @Test
               | public void test() throws Exception {
               | Stream<String> stream = Stream.of("foo", "bar", "baz");
               | stream = apply(stream::flatMap, s -> Stream.of(s + "_a",
               | s + "_b"));
               | System.out.println(stream.collect(toList()));
               | Optional<String> bean = Optional.of("bean");
               | bean = apply(bean::flatMap, b -> Optional.of("soy"));
               | System.out.println(bean);
               | CompletableFuture<String> future1 =
               | CompletableFuture.supplyAsync(() -> "hello");
               | CompletableFuture<String> future2 =
               | apply(future1::thenCompose, string ->
               | CompletableFuture.supplyAsync(() -> string + " world"));
               | System.out.println(future2.get());         }
               | 
               | Output:
               | 
               | [foo_a, foo_b, bar_a, bar_b, baz_a, baz_b]
               | 
               | Optional[soy]
               | 
               | hello world
        
               | mrkeen wrote:
               | This is interesting stuff.
               | 
               | I'm trying to figure out how to refer to flatMap directly
               | from the abstract function, e.g.
               | flatMapTwice(x, fun) {             return
               | x.flatMap(fun).flatMap(fun);         }
               | 
               | or                   chain(x, fun1, fun2) {
               | return x.flatMap(fun1).flatMap(fun2);         }
               | 
               | It should also preserve the type information (just like
               | you did above). I don't want the caller to need to
               | downcast from FlatMappable.
               | 
               | It doesn't need to be x.flatMap(f) - I'd be interested
               | seeing a flatMap(x, f) version.
        
               | [deleted]
        
         | kasperni wrote:
         | A Parametric VM [1] is part of Java's project Valhalla.
         | However, it is currently some releases away.
         | 
         | [1] https://cr.openjdk.java.net/~jrose/values/parametric-vm.pdf
        
           | _old_dude_ wrote:
           | Very cool, it seems to be the best of both worlds, the
           | specialization of the class/fields is done the first time a
           | specialized type is instantiated while the specialization of
           | methods is done by the JIT.
           | 
           | And the VM can specialize on any values, not just types.
        
         | dep_b wrote:
         | There is no next step for Java, because of backwards
         | compatibility this is as good as it gets.
        
           | Felk wrote:
           | While it does need to maintain backwards compatibility,
           | Project Valhalla seems promising on improving on some of Java
           | Generic's pain points. So it's not a dead end quite yet
        
         | kaba0 wrote:
         | I don't think templates are wanted, but with the coming of
         | unification of primitive and reference types, Java could get a
         | huge speedup in performance.
        
           | henryjcee wrote:
           | This has passed me by, what's happening?
        
             | BenoitP wrote:
             | Not so much a unification, as the new constructs sort of
             | enhance and retrofit current primitives types. These new
             | primitives are "class instances that lack object identity
             | and can be stored and passed directly, without object
             | headers or indirections".
             | 
             | It will allow (almost) direct control over data layout. At
             | least make it so that it is cache/prefetch/SIMD friendly.
             | 
             | Here is the JEP: https://openjdk.java.net/jeps/401
        
       | dep_b wrote:
       | I really don't enjoy anything about Java except for the JVM and
       | if I press somebody that advocates for Java about what the
       | reasons are that I should use Java it's always about either the
       | JVM or the fact that there's so much of it around. But never
       | really a solid argument why the language itself is nice to work
       | with.
       | 
       | If I would want to use something like Java for a project, I would
       | always pick C# which is a better Java in all aspects.
        
         | skocznymroczny wrote:
         | The relative simplicity of the language allows it to have
         | excellent IDE support. I can select a class name, rename it,
         | and have it renamed in all files that use this class. And the
         | IDE knows that this actual class is being used, not another
         | class from another module that has the same name, and that it's
         | not a string with the same value as the class name. With
         | languages like C++ all you can do is grep and cry.
        
           | Semaphor wrote:
           | OP mentioned C#. Exactly the same is possible there.
        
             | gmueckl wrote:
             | Yes and no. The C# language does have conditional
             | compilation, which is modelled after the C preprocessor.
             | That can interfere with refactoring and limit its accuracy.
             | The good thing is that this mechanism is rarely used in
             | practice.
        
               | shellac wrote:
               | Does the LINQ expression tree stuff complicate things? I
               | don't know how that works with respect to parsing.
        
           | northwindfoo wrote:
           | Visual studio has c++ symbol rename, including class names.
           | Has had it for a while as well. Fairly reliable, though
           | occasionally it does fail, loudly.
        
         | ludovicianul wrote:
         | Why does it always has to be language 1 vs language 2? "Better"
         | is hardly something objective about languages. This is why
         | people are still building new languages every year. To make
         | things "better", although ultimately a bunch of others will
         | find quite a big list of why they don't like it. Just pick
         | whatever language you like. As long as you have good tooling
         | and community around it, it won't be neither the best nor the
         | worst choice... It's just "a choice"... Projects hardly fail
         | because of the language itself (although there are some well
         | known failures with dynamically typed languages). It's mostly
         | bad architectural or business decisions...
        
           | hackerbrother wrote:
           | I think that in comparison, what happened with Javascript has
           | been great honestly. People have been completely stuck with
           | it, so we've built a great server side (Node.JS / npm / yarn)
           | and client side (React, Vue, Typescript, Babel) ecosystem
           | around it. I think great things can happen if you stick with
           | a language and work with that limitation.
        
             | thrower123 wrote:
             | Things will be really exciting once WebAssembly finally
             | breaks through (Next year in Jerusalem!...) and we're not
             | forced to compile better programming languages to
             | Javascript.
        
         | davnicwil wrote:
         | > a solid argument why the language itself is nice to work with
         | 
         | Java with IntelliJ is _awesomely_ productive. You barely have
         | to type anything beyond new variable  / field names, everything
         | else is generated or tab completed. Refactoring is a breeze.
         | 
         | That, times 'worse is better' - it's the language and toolset I
         | already know for the types of things it's good at. I have no
         | doubt C# is comparable or better at most things, but probably
         | not so much so that it is worth switching.
        
           | Semaphor wrote:
           | > Java with IntelliJ is awesomely productive.
           | 
           | They have a C# IDE (Rider) now, based on IntelliJ. While it
           | was originally the only non-Windows C# IDE, I and others have
           | switched to it from Visual Studio (which is almost as good)
           | even on Windows.
           | 
           | > I have no doubt C# is comparable or better at most things,
           | but probably not so much so that it is worth switching.
           | 
           | I'd say you are right. Now. 10 years or so ago (6/7/8 times)
           | I'd have disagreed, C# avoided many of the mistakes* Java
           | made and was able to become far more pleasurable to use, it
           | took some time for Java to catch up again.
           | 
           | *: Mistakes only in hindsight
        
         | pjmlp wrote:
         | > But never really a solid argument why the language itself is
         | nice to work with.
         | 
         | If you were trying to write portable C++ code in 1996 you would
         | get it, specially when using each OS specific C++ compiler
         | provided by the platform vendor.
        
           | codeulike wrote:
           | Exactly, at the time it was invented, Java was a massive
           | improvement on the incumbents in all sorts of ways
        
           | AnimalMuppet wrote:
           | We cross-compiled to get gcc on every platform. Even then, it
           | was still:                  #ifdef _AIX        ...
           | #else ifdef SGI        ...        #else ifdef WINDOWS
           | ...        #else // everything else        ...        #endif
           | 
           | Shudder.
           | 
           | But the other thing that Java had is the _library_. Compared
           | to C++ 's standard library in 1996, it was a revelation.
        
         | thrower123 wrote:
         | From what I have seen, I'd probably pick Kotlin if I had to be
         | on the JVM. Seems to strike a nice balance between sanding off
         | the rough edges of Java syntax, and still being conventional
         | and blub enough that it won't put people off.
        
           | xwolfi wrote:
           | Not sure everyone agrees but each time I've used type
           | inference language (really just Groovy to be honest, but many
           | many many different teams and companies), it ended up with an
           | enormous mess of code that the brain of all of us devs had to
           | parse themselves. We rolled back to Java many such critical
           | paths just to be able to maintain them.
           | 
           | My little pitch when starting a migration project in a
           | distressed no-type language team is to make them read the
           | code and explain it, while asking about the type of objects
           | they use and show them when they read it's their brain
           | parsing it with high effort. If they de-optimize and just
           | write the types more often, it becomes much clearer longer.
           | 
           | You can type usually in those language but often people want
           | to use the "proper way" as shown in ultra simplistic examples
           | in manuals for sales pitches and end up thinking the right
           | way to code is to write as little as possible (combined with
           | the no-comment belief they have but don't map to "no comment
           | because clear enough code").
        
             | thrower123 wrote:
             | I mean, Groovy is terrible, and there is not very good
             | tooling for it.
             | 
             | That's not the case for Kotlin, that has the full weight of
             | JetBrains behind it.
        
           | dboreham wrote:
           | Are you assigning any cost to : Kotlin is not Java and
           | therefore new tools, developer training, etc are required?
           | I'd want Kotlin to be significantly better than Java to
           | justify the fact that it's not Java, if you see what I mean.
        
             | dep_b wrote:
             | You can write Java in Kotlin in less than a day.
        
         | xwolfi wrote:
         | Well I may try ! I'm not very religious, I used Java for
         | everything since I've started programming and I work in low
         | latency in an investment bank now. I adore Java, it's my truly
         | native programming language, I try to know all of its quirks,
         | study each new version and know still not enough to satisfy my
         | love of it.
         | 
         | I also did a lot of C# to help on a GUI optimization project
         | (because doing low latency if the GUI freezes every 10 minutes
         | isn't great), and I was really surprised by how good it was so
         | I wouldn't disagree it works nice.
         | 
         | BUT I like that Java works simply in linux, I like that it
         | didn't yet got contaminated by all the crazy constructs from
         | elsewhere (Groovy grr), I love the tradition around the syntax
         | (I can't stand C# quirks sometimes). I do HATE with passion the
         | meaningless generics, and I wish we'd ditch backward compat to
         | steal it all form C++ templates.
         | 
         | I enjoy more than the language syntax itself that is close to
         | most languages I like (Javascript, C#, C++), the team spirit
         | around it. Believe me, it's much easier to use and understand
         | maven (or maybe gradle who starts to get dangerous in real
         | world projects where you can't afford 2 weeks of reverse
         | engineering a madman's build script) than it was nugget, it's
         | easier to use IntelliJ than to receive yet another stupid email
         | from compliance about a Visual Studio license missing, it's
         | nicer to build a jar and run it than to go through all the
         | kinks of exe files... I dunno I may have started from Java and
         | got to C# and done the opposite of your journey, but while I
         | agree C# has superior features in some aspects (but sometimes
         | they chose mess vs safety) I just wasn't convinced it could
         | work for something more critical where you must do things as
         | simple as you can, as clear as you can.
         | 
         | And let's not even start on C++ who's superior in every
         | conceivable way except that it encourage, or doesn't
         | discourage, every team I ever meet to create unbearable messes
         | they can't fix, so focused they are fixing problems we can't
         | even imagine in Java for reasons we can't comprehend our
         | company even accepts to finance :D
        
         | shellac wrote:
         | Your first paragraph is a fair encapsulation of what Java was
         | intended to be from 1994: a mullet of a language that is
         | deliberately unexciting at the language end, but brings in a
         | number of (at the time exotic) features at the VM end.
         | 
         | Things like GC, reflection, runtime loading of code, that were
         | very familiar from more academic languages at the time.
        
         | overtomanu wrote:
         | Backward compatibility is one of the biggest reasons
        
         | mrkeen wrote:
         | Does C# handle generics better than Java?
        
           | LandR wrote:
           | C# generics are reified.
        
           | accountLost wrote:
           | You can't easily create a instance of the generic type in
           | java. for example :
           | 
           | return new T();
           | 
           | is possible in C#, not in java.
        
             | ohgodplsno wrote:
             | There is a way-ish, which is passing a Class<T> as an
             | additional parameter and doing clazz.newInstance().
             | 
             | Definitely not pleasant though.
        
               | Felk wrote:
               | For that particular use case it may be better to pass a
               | constructor reference / new instance supplier to not rely
               | on reflection.
        
           | Felk wrote:
           | As "better" isn't clearly defined, here are some things that
           | some people might consider superior:
           | 
           | - primitives or any custom value types/structs can be used.
           | No need for boxing or method duplication for primitives. -
           | type information available at runtime, no need to pass
           | 'Class<T>' parameters. - type constraint 'new' available,
           | which means it must have a default constructor so you can do
           | 'new T()'. No need to use reflection or pass a constructor
           | parameter. - type constraint 'notnull' available - class-
           | scoped compiler enforced covariance and contravariance,
           | though I think you can get similar results with java's "?
           | extends T" and "? super T" constraints
        
           | paavohtl wrote:
           | C# implements proper reified generics. Generic types
           | parameterised with different types are actual types in C#,
           | while in Java generics is basically just syntax sugar for
           | avoiding type casts. Each element in a List<byte> in C# takes
           | 8 bits, while in Java each element in a ArrayList<Byte> is
           | actually just an Object and therefore is a pointer (64 bits)
           | to a separate allocation.
        
             | kaba0 wrote:
             | But at the same time, reified types will create a separate
             | object for each template type, making some optimizations
             | happen only for a type that was used often. While in Java,
             | ArrayList for example will benefit regardless of the
             | concrete type.
        
         | HappySweeney wrote:
         | Java has a much better AOP story, but that will likely change
         | once source generators become more mature.
        
           | xwolfi wrote:
           | I find AOP dangerous. I love the abstract concept, but each
           | time I used it myself I ended up creating a mess that I had
           | to painfully rollback just to produce less insanity for my
           | poor colleagues.
           | 
           | I'm sure it's me but do you use it yourself and why ? Don't
           | you get lost in the indirections in big projects "oh shit
           | where is that thing again that auto inject itself here" ?
        
       | xwolfi wrote:
       | One flaw of the article, that is indeed interesting, is that it
       | seems to ask us to accept a fact that serves little purpose.
       | Yeah, we understand why the designers made the choice because it
       | was the best for them, and they did think a bit of their users,
       | but just a bit.
       | 
       | Decades later, we're still wondering as we get to more and more
       | complex use cases, why we're stuck with passing the class object
       | to all our generic types just to know who we are. It's really a
       | rare instance where Java gave an engineer's solution when they
       | should have given us something more, like a compile flag, a fork,
       | a GenericArrayList<Something> that would be reifed, anything,
       | rather than excuses about ecosystem, backward compatibility
       | perfection, etc.
       | 
       | I understand why they took that decision, I don't understand how
       | it helps all of us do meaningful generics :(
        
       | datavirtue wrote:
       | This is a great write-up. Accessible but thorough.
        
       | ChrisArchitect wrote:
       | alternate url also submitted 9 months ago, I dunno
       | 
       | https://news.ycombinator.com/item?id=23621185
        
       ___________________________________________________________________
       (page generated 2021-04-01 23:03 UTC)