Hacker News new | past | comments | ask | show | jobs | submit login
Modern Java – A Guide to Java 8 (github.com/winterbe)
404 points by aronhunt470 on Nov 16, 2015 | hide | past | favorite | 197 comments

My Java code has improve dramatically since I started (ab)using Optionals.

  String foo = Optional.ofNullable(paramater)
                       .map(a -> ...)
                       .map(b -> ...)
                       .orElseGet(() -> ...)
Between this kind of thing, and similar playing with Streams, some of the ugliest code I work with is suddenly quite clear, coherent, understandable.

Hear hear. A couple more features that'd be nice:

- An `public Optional<T> or(Optional<T> other)` so if optA is empty, then `optA.or(optB)` would return `optB`

- A `toStream` method on `Optional` (I believe this is coming in Java 9) or have a `Stream.flatMap` signature that flattens `Optional`s without needing to call `Optional.toStream` (Scala does this, arguable if this is an acceptable munge of Monadic types, but it's really convenient)

Check out fugue: https://bitbucket.org/atlassian/fugue -- a proper monadic Option and more. :) (Full disclosure: I'm an Atlassian and I know/<3 the people who write it, but I also use it a fuckton in my own code.)

Yea this library with Java 8 makes functional style java actually kinda nice. The addition of an Either really opens up what you can do.

Welcome to the world of functional programming =D. I personally (and professionally) suggest moving toward Scala when you feel that itch to dig deeper into FP.

Ugh, no. Skip Scala, which is on the decline and headed fast toward obsolescence, and pick a post-Scala language such as Kotlin or Ceylon.

Scala is not in decline - as far as I can see it's on the up and up. Scala has its share of warts but that comes with maturity - I would lay money that Kotlin won't look so nice when it's gone through a similar amount of change.

Ceylon OTOH is at least good design, though I don't think that outweighs the library and tool ecosystem that Scala has.

Definitely not true. Scala is at its strongest ever. It's now possible to write frontends, backends, and mobile applications in Scala. There's an incredibly diverse and growing ecosystem of libraries. The Scala tooling has really stepped up.

The fact that there's so much discussion on the new SIP (Scala improvement process) and SLIP (Scala library improvement process) issues is a testament that Scala is alive and well. This is what happens in a mature language: many people put stakes in the ground.

Also, Scala most certainly has a bright future to go with its vibrant present. EPFL is hard at work with dotty, which will be a simplification and improvement of Scala.

Could you point an interested reader towards some stats :) ? I wanted to pick up Frege for a hobby project some weekend soon and would love to read more about the JVM up-and-comers.

No, I'll pick the next next one.

Two things. First, why is it better than:

    String foo;
    if (parameter != null) {
        foo = ... ;
    else {
        foo = defaultValue;
or even this, if you express all your transformations as a single expression:

    String foo = parameter != null
        ? ...
        : defaultValue
I don't get it. Your way of doing seem awfully more verbose. Please note that I'm not against Optional in general (although they are overplayed in my opinion).

Second thing, I can't shake off the feeling of inefficiency: building these closures up, calling them. Lots of indirection for something that's probably very simple. I wouldn't want to call this code in a hot loop. But then maybe it's not in a hot loop. Before you bring that up, the sufficiently smart (JIT) compiler doesn't exist. "Sometimes suprisingly dumb" is a better qualifier for many compilers.

Note that I work with Java, and use the Java 8 features a lot. I just think this isn't a really good example.

Actually, your way is awfully more verbose, but you didn't notice it because you ellipsized the verbosity.

If you actually take the time to translate OP's code with your own, you will soon find yourself indented six levels with "if (a != null) { ... if (b != null) { ... if (c != null) {...

You get the idea.

Monads (the type class that Optional belongs to) flatten all this boiler plate with a function called... flatMap! And you don't even need to know about it, all you need to do is chain the calls like OP did:

                       .map(a -> ...)
                       .map(b -> ...)
                       .orElseGet(() -> ...)
without having to check against null every step along the way.

Point taken. I'll tentatively argue if all your functions can return null you have other problems to contend with, but what you said makes sense.

I'm not so up on Java, could someone explain why they chose "->" for lambdas instead of the "=>" that C# and ES6 use, as those languages seem to me like they would be the first place to look for inspiration.

AFAIK that's actually non-standard. Haskell and (more importantly, maybe) C++11 use the single-line arrow (->), as do Erlang and various ML dialects (interestingly, F# uses the single-line notation, too, while C# does not). Probably other languages as well.

In addition to the other answers, I bet it's easier for a CFG to parse knowing that "->" can't also be "negate and greater than" (since negation is only a prefix op) whereas "=>" can easily be a fat arrow or greater-than-or-equal.

Why would that be any harder? The tokens are different.

True, Java does not support => as an alternative for >= so the ambiguity does not exist. It was just a guess, but since it was probably not right, I decided to dig. This mail post[1] explains that the extra equals sign becomes a bit annoying w/ other equal signs in the same expression.

1 - http://mail.openjdk.java.net/pipermail/lambda-dev/2011-Septe...

I have no idea why Java chose that, but thought it worth mentioning "->" has been in use for Ruby longer than the alternative .NET and ES6 syntax.

While I don't know the reason i like it because for me is easier to type -> than => (portuguese keyboard, TBH dont know the difference from english)

The - and = are right next to each other on an English keyboard.

I'm not an expert. C++ uses -> for pointer dereference, e.g. o->p refers to the property "p" of object "o". I would assume C# inherits from C++ and had to define another operator.

Java probably followed Scala's notation, which is probably derived from mathematics where ↦ is an application (a function) and ⇒ is a simple logical implication.

Actually Scala uses =>, so Java went the opposite way. Kotlin also uses ->.

Doesn't matter much in the long run, you'll get used to whichever your language favors after just a few days or writing code in it.

Two .map-s one after another is code smell (and also a bit slow). Why not combine them into one?

Do standard collections have groupBy? You can go a long way with groupBy and zipWith operations, which are a pair like map-reduce but for in-memory handling.

Why two .map operations? Three reasons.

#1- If I'm dealing with some legacy code that I don't know well, and it's mapping from one object to a property of it that is another object... well, I'm not 100% certain it's not null.

   .map(a -> a.getB())
   .map(b -> b.getC())
if a.getB() is null, Optional eats it and I'm safe from NPE- I just an a non present Optional value.

   .map(a -> a.getB().getC())
If B is null, I'm going to NPE here (... right? I'm like 99% sure of that, but correct me please). And there are counter arguments like "never be null", etc, but I've got legacy code to cleanup, and no one taught the person who wrote it that particular lesson.

#2- Ease of understanding. Little jumps rather than big ones. Makes the code easier to understand for the next guy who has to work on it. As much as I can, I try to write code that requires no commenting- because it's so clean it's not needed.

#3- The compiler will (or will eventually) optimize it into a single map anyways. Compilers are smart and keep getting smarter. So long as my big-O runtime is fine, I just focus on ease of understanding of my code.

>Two .map-s one after another is code smell (and also a bit slow). Why not combine them into one?

I believe they are automatically combined before the results of the stream are forced.

It might be simplified this this example, but if the mapping function is a method reference or some other reusable lambda, then it can be clearer to use consecutive .map-s

I believe this use is a real overuse and is definitely abusing. Sure, use optional as a return type when clarity is needed, but otherwise don't do all of this when you could use a simple conditional ala:

    String foo = defaultValue;
    if (parameter != null && ...) {
      foo = doAAndB(parameter);

Does that really look better to you? More understandable? Methods with the word "and" in them are really awful smells to me too.

Comparing your code and mine, I ask you this: which one do you thing is more maintainable after 5 years of edits?

Each time a new developer needs to add a new case, a new rule, a new whatever, on my Optional (ab)usage, they just add a new line in the appropriate spot, a .filter or a .map or whatever. In yours it's far less clear.

Optional, for me, is wonderful because it lets me have a single return statement. A lot of the code in Java I'm replacing with this pattern is the stuff that has 5 different returns after checks. Crusty old stuff, but the business logic it's running has grown the way it has for good reasons.

Java does not have a full FP story, so to bastardize newer code as though it does becomes unreadable IMO. The "and" was just for the example, since "..." wouldn't have made it clear. I do not find it more readable personally, but that is a personal opinion. Because other code eagerly returns (doesn't usually bother me in an imperative language) is a different problem with code path complexity that can be addressed elsewhere. There is still a practicality in normal conditionals and traditional linear paths.

I don't disagree that FP doesn't feel complete in Java yet. But that doesn't mean I'm not totally willing to start playing with what I've got!

The biggest worry I have right now is the story on debugging code with Optional bastardized FP. I know I'm going to learn some lessons soon enough, lol.

I've always had some sort of mistrust of Java (neo-Cobol ohnoes!) but I'm liking it more and more. Mostly I've come around to the fact that static typing isn't as bad as my young self thought (and can in fact be quite cool). The verbosity and "enterprisy" and committee oriented feel are still a bit off putting but I've made a commitment to try some Java on a couple of weekends. Did a bit of research and there's actually nice looking lean web-stuff, too (Spark Framework, Ratpack). Played with the J8 features back when it was released and they are pretty cool.

Cliffnotes: I think I have discriminated against Java mostly on a "religious" basis. I think it's a great time for people like me to give it another shot (especially if you have grown accustomed to functional ideas form other languages in the meantime).

The repo is an excellent resource, bookmarked.

Good luck with your experiments.

Many of us in the 90's jumped into Java, in spite of its issues, because it made it more pleasing to write cross platform code, than using C or C++ with CORBA/COM across OSes with compilers that were still playing catchup with the standards.

I never understood the following : Eiffel had a better syntax, better support for core issues, Void type, design by contract, multiple-inheritance and an intermediate virtual machine for portability although the most common target was C code. To name a few. I was using Eiffel in a small side project just about when Java released their 1.1.4 (a version before the object serialization library, iirc). It was a sticking point for me. Add to that, to program in java before IBM Visual Age was released was to using vi/notepad(++?) editors. Coming from Smalltalk env that was a bit of a shock. Eiffel commercial IDE from ISE was way better and its clever use of pick event on the mouse's right click to drop to compile/run targets was actually easier on my fingers, which is a side show, but good quality products demonstrate quality at unexpected places.

I am still programming in java in my day job, knowing fully well that Java was not my top five language choices. Eiffel's type system was a bit more advanced than Java even way back in those days, so not only was I stuck without an ide till VA for Java showed up or VisuallJ++ I also had to accept weak inheritance and design by contract models. Performance of course was not even close to an Eiffel compiled C code, which was probably addressed in later versions of java.

My point being, that just as Eiffel was something that was better and set aside by a few leading development shops, there must have been other languages that could/should have received a fair share in language evaluation by programmers, I dont necessarily mean limiting to Ruby, Python for example, despite them being excellent tools, they may fall short in an enterprise ecosystem. What happened at least I seem to think instead is a sort of groupthink to start coding in Java because of rubbernecking. All in all, I think that things could have been better if people paused to understand the Java language model and fixed in java 1.2/3, or some earlier version so we didnt have to wait till Java 1.8 to get these features. /rant.

I developed a great interest in Eiffel after I read Bertrand Meyer's book "Object Oriented Software Construction" (which I still think is one of the all time great books on object-oriented programming), but I never found anyone else that was using it and the IDE was way too expensive for me at the time. It just seemed to me like one of those technologies you'd probably get to have fun with on some niche project at a defense contractor, but that you'd probably never see out in the more general software development world.

I was one of those folks who actually bought the personal license. Over years, SmallEiffel came along and improved the ecosystem. Of course, now Eiffel has a community/enterprise model. In any event, I do think that they were pricing them out of the market. Defense contractors, seem to like ada more. The feature that I miss from Eiffel is the Expanded (allocated on stack feature). Even haskell has some #untyped to deal with boxing primitives. I thought that Expanded was a stroke of genius: bringing specification to implementation without the middleman(aka heap).

I agree with you.

Before Java was known to the world, I already knew plenty of languages with closures, value types, GC,AOT compilation to native code, ...

But they all suffered from two problems, not being from known companies with geek credit and being commercial.

Java was from Sun (geek credit) and was free (as in beer), hence why it got adopted.

I think it would been great if Eiffel had got a decent market share,instead of just the DBC ideas.

But if I remember correctly they went after the enterprise, so the language was out of reach to many developers given what they used to charge for.

Syntax is not a big deal. Ideally it should be small. If you don't like java, please don't use it. move on. Why create ugliness in java?

Typesystem is not the same as syntax? I dont believe raising ugliness in syntax as an issue. Of course, I do mention the ugliness in Haskell and that is in special cases where the overhead of boxing is significant. So, yeah, we could move on, but I think that is a specious argument. I guess one could really move on, till we start seeing Scala embrace Uniform Access Principle or Java embrace lambdas (implicitly or explicitly) to support generics. So, what it seems to me is that you are ok with a bad language design and accept the weakness of the language because it was delivered and also accept that like any working document there could be amendments and that is part of working in a programming language. Moreover, the principles of language design that I list were available as prior art, so it is not clear as to why Java language designers decided to at least not refer to it during design. Anyway, my intention was not to start a flame war? Note: Regarding : "why create ugliness in Java?" Any reason for an ad homenim attack? or was it a general rant? Since we are on hacker news I would like to give you the benefit of doubt :).

I don't remember the detail at the time but I think the lack of free tools hurt Eiffel's wider adoption.

I had some little experiences with Java and now I'm approaching C# so I have a genuine newbie question regarding cross platform development: was Mono (or .NET core) a thing in the 90's, would you have chosen C# over Java?

> Mostly I've come around to the fact that static typing isn't as bad as my young self thought

That's not exactly a point for Java though, considering it doesn't really have a good static type system.

Could you define good?

Java's way of expressing variance is really awkward (even quite popular libraries get their types wrong as a result), arrays are special-cased into unsoundness, Serializable is a total mess where it could be a perfect example of how to use a type system, there are no higher kinds, the exception system is like a second parallel type system (it even has union types, which would be incredibly useful if you could use them in the rest of the language). More than that, the standard library doesn't really use the type system to its full effect, e.g. errors in JDBC are C-style numeric codes, Comparable returns an int for what should be a 3-value enum.

(To answer your question, a type system should be sound unless and until the user explicitly requests otherwise, easy to use, but above all consistent)

Besides null being a potential type for any reference and causing a ton of issues I do not think most Java programmers regularly care about the lack of abstractions like higher-kinded or union types. Would be nice to have, but never have I felt like I needed them.

Enum types didn't exist in Java when the Comparable interface was implemented. That's why some API's look old-school by comparison because newer language features didn't exist yet.

> I do not think most Java programmers regularly care about the lack of abstractions like higher-kinded or union types. Would be nice to have, but never have I felt like I needed them.

That could be stated about pretty much every feature added since Java 1 really, it's not like the blub paradox has disappeared from our reality. How do you know that you wanted union types when you've never used a language which had them (and had features hindering such a conceptualisation, C-style enums are not really helpful there, even less so when extended the way Java did)

> I do not think most Java programmers regularly care about the lack of abstractions like higher-kinded or union types. Would be nice to have, but never have I felt like I needed them.

My counterargument would be the popularity of annotation-based libraries in Java. An annotation is basically a place where the official type system has proven inadequate.

That's not entirely true, part of annotation's use case is metasyntactic: codegen, code analysis and just plain structured/extractable comments.

For an eye-opening experience, walk through the tour of Ceylon sometime. It has the most sophisticated implementation union & intersection types I've yet seen:


Compared to union/intersection types, I find myself craving higher kinded types less frequently. I do feel the absence strongly, however, when I'm writing tools. I think the lack of higher kinded types manifests in the Java community as inferior libraries (eg ORMs) that require more manual-feeding of information via annotations or extra parameters to methods.

> I do not think most Java programmers regularly care about the lack of abstractions like higher-kinded or union types.

When you become used to a given language, you start to reason in its terms. A programmer who is used to Java is used to its shortcomings and will not miss Lisp macros or dynamic types.

> Comparable returns an int for what should be a 3-value enum.

To be fair, I can think of another functional language starting with "O" guilty of exactly the same thing...

I was about to refute your argument by proposing creating a union type. Here is a relevant reference: https://realworldocaml.org/v1/en/html/functors.html

(This idiom is a bit of a historical error. It would be better if compare returned a variant with three cases for less than, greater than, and equal. But it's a well-established idiom at this point, and unlikely to change.)

Eiffel is similar to ocaml : http://www.infor.uva.es/~felix/referencia_smart_eiffel/libra...

Does anyone know the advantages of the above signature over the Haskell alternative? Because this seems to be more obvious and easier to read. So there are more than one language guilty of using a ternary operator for compare.

Haskell does define the appropriate constructors, rightly so: https://hackage.haskell.org/package/base-

data Ordering :: *

Constructors LT EQ GT

> Does anyone know the advantages of the above signature over the Haskell alternative?

Historical reasons, mostly. The only practical advantage I can imagine is that it's a bit shorter to implement a reverse comparison, since you just multiply by -1.

This is a really nice overview of the new Java 8 features. I like how this tutorial communicates mostly with code snippets and has "read more" links for most topics.

If you are using Java and have not tried Lambda Expressions, method references and default methods, do so now! After that, you'll think "How was I able to live without that?".

I also think that the way Java integrates lambda expressions (via functional interfaces) is really nice. Sure, you have to write some boilerplate code in some scenarios. But since they re-used interfaces and did not event some "function" type (or similar), it fits really nicely into the existing language.

If you don't know Java, this tutorial will probably not be enough - But that probably wasn't the intentian anyway. It's a great intro to what has changed with Java 8 (and all those changes are real improvements for the Java language, IMHO).

If you are using Java and have not tried Lambda Expressions, method references and default methods, do so now! After that, you'll think "How was I able to live without that?"

I think instead of "How was I able to live without that?", people are more likely to exclaim "about time!"

Well... depends on who you ask. I, personally, was more in the "about time!" camp. But I have met several programmers who didn't think they'd need those "newfangled features" at first. Especially in the "early days" of Java 8... And sometimes I still meet people like that. So that scentence was targeting those developers that are still stuck with Java 5 or 6 or 7 and have never tried Java 8.

Also, I was once co-teaching a C# workshop where we showed people how to work with LINQ and extension methods (long, long time ago ;) ). Some participants asked us if there was a way to configure the compiler to forbid those features for their team.

Not everybody likes "modern" features, I guess ;)

Well technically they didn't need them, no - I've been working with for and for-in loops for years before learning other languages (like Scala, JS) that had a .map function that could do the same thing but much more compact and expressive. So their argument isn't wholly invalid, and TBF I'd rather see a regular for-in loop than my colleague's atrocious 200-character oneliners.

I have that experience with lots of companies:

Very little LINQ usage, usually Java 6 still the latest version on the servers, C++ code older than ANSI C++98....

Yep, somewhere at Xerox PARC in the early 80's

    coll := ((1 to: 100) select: [:x| ( x * x ) > 3]) collect: [:x | (x * 2)].

What language is this exactly? I'd love to research a bit more. Just any search terms to point me in the right direction.


And a whole world of Standard ML in the early 90's

Java lambdas are no different than an anonymous Runnable or Callable. It's just nicer syntax.

> It's just nicer syntax.

Which is mostly the point. Ultimately, everything is no different from <some lower tech thing doing the same thing>.

Exactly. Everything is just syntax sugar over 0s and 1s :)

they are implemented in a different way, using dynamic invocation. the practical implications of the implementation don't come up too often but they are there, so it's probably good to understand.

Depends where on the Blub scale you are. For those above java, sure.

Modern here obviously doesn't mean "Java is modern", but rather "the modern form of Java"

Did you intend to answer to my comment? I never used the word "modern" :)

> maps don't support streams

This is very misleading. There is not a #stream method on Map because Map supports three different streams:


Thanks for your feedback. I've improved the section about maps to better reflect the capabilities of streaming maps:


Thanks for your hard work here!

Side note: http://winterbe.com looks quite similar to angel.co. Or does angel.co look quite similar to your website? :-)

Dunno. I wrote the whole css for winterbe.com from scratch. To be honest I don't find both pages like similar besides white background and gray subtitles. :-)

Furthermore, Maps have a .forEach method

m.forEach((key, value) -> System.out.println("key: " + key + " value: " + value));

Those methods are not on Map, but on Set and Collection.

But those are streams on sets.

Those are streams on views on the map. My complaint is that a reader seeing "maps don't support streams" with no further explanation is unlikely to get the correct idea, and may infer that something like

  map.entrySet().stream().filter(e -> e.getKey().equals(e.getValue())).findAny()
is not expressible.

They're streams on map views, a stream on Map itself doesn't really make sense because there are multiple ways to interpret a map as a sequence and it's not clear what should be the default (e.g. Python defaults to the equivalent of keySet().stream(), but Rust defaults to entrySet().stream()).

keySet() and entrySet() return Set-conforming objects because they can, values() can't and only returns a Collection, but these three objects delegate much of their logic to the map itself.

This is a really nice write up!

Java 8 is such a huge improvement over previous versions. One thing it still lacks is a better literal format for defining maps, etc.

I just self published a book that uses Java 8 (https://leanpub.com/powerjava if you will pardon a plug) and to be honest I got some pushback from a few people who like my books as to why I didn't use Clojure, Haskell, etc. To be honest I do prefer Clojure and Haskell, but the Java ecosystem is so huge, with so many good libraries that sometimes it is the more practical language to use.

Mark, I am sure you know this, but I am posting this for the benefit of others who might not.

For literal maps, I tend to use double brace initialization style. It is more verbose than languages with direct literal support for maps and creates an extra anonymous class, but it keeps the map in a single syntactic construct. For lists, I use Arrays.asList(a,b,c) style; if you static import the method it reads nicely as-list-a-b-c.

Which is a no-go in Android as it can prevent GC in some cases, because as anonymous inner class it holds a reference to its parent and you end up leaking Activities.

It is usually presented as anti-pattern in talks about Android performance.

Ah, I have never programed for Android devices. Thanks for pointing this out.

Guava has some tools that make map literals a little prettier:

  Map<Foo, Bar> map = ImmutableMap.of(foo1, bar1, foo2, bar2, foo3, bar3, foo4, bar4);
There are overloads for up to 5 entries, and they're even key/value typesafe.

I personally jumped right into Clojure on the jvm without much Java experience. I've found it to be awesome. Is there a non-political reason that someone should choose Java over Clojure (or possibly Scala)? Where does Java shine?

There's a gravitational pull towards popular languages because popular languages have more books, more (idiomatic) libraries and tools, copy-pasteble examples all over the web and a big pool of developers that can be productive from day 1 on simple problems.

In order to adopt a language that isn't in the top 6 or so, you've got to have good reasons for it. And depending on the problems you're trying to solve, sometimes it's worth it, sometimes it isn't. For a startup, with a small but capable team that's trying to outrun its competition to market, then using the most productive tools possible is a good choice. In other contexts though, like in a big company where long-term maintainability might be more important than speed to market, well, Java might be the better choice, and not because of the language per se (I think Java code is often unreadable), but more because the cost of maintainability is low due to its popularity and its slow pace of change.

I don't know how you can find even average Java code unreadable. If anything it's incredibly verbose. Only now with the most recent features is it really likely that you'll run into some one-liners and magic functions. Now, if you were talking about frameworks/app servers and not being able to figure out which levers and knobs to push to get them to behave, I would agree wholeheartedly.

I'm not saying Java is good looking by any means, but I've never found it to be heard to read in the "I can't completely grasp exactly what everything is supposed to do" kind of way. I often find Python and JS to have enough shortcuts, callbacks, and anonymous functions to find other people's code much harder to grok than other people's Java.

> I don't know how you can find even average Java code unreadable. If anything it's incredibly verbose.

Not bad_user, obviously, but excessive verbosity can be antithetical to readability. (As can excessive conciseness.)

It can make the overall structure and meaning of the code hard to discern, even though can clearly tell where what gets assigned to what variables, etc.

The thing that always kills me on new Java projects is wading through these enormous hierarchies of folders full of interface classes just to find one actual line of implementation code. Of course, right after that one line of implementation code there's an invocation of some other interface class that then leads me on the next goose chase to find line 2. I think it's not so much a problem of verbosity as it is this sort of "Enterprise FizzBuzz" mentality that crept into Java over the years. Some of this IOC stuff gets so far out of hand that I feel like I'm in the House of Mirrors at the local carnival when I first start trying to walk through a new code base.

Hence why IDEs with their code navigation tools rule.

Yeah, I don't know. Java developers need IDEs like Rob Ford needs crack cocaine, but it's more a comment on the deficiencies of the language than the purported benefits of an IDE.

IDEs were created for Interlisp-D, Smalltalk and Mesa/Cedar.

Were popularized in the PC, Amiga and NeXT eco-systems, thanks to Turbo Pascal, Delphi, VB, AMOS, GFA, Objective-C, Eiffel, Oberon, ...

Java wasn't even a thing in those days.

That's not the point. Due to its verbosity, it's impractical to use Java without an IDE. That's not necessarily the case for more compact languages.

Programs are written once, read multiple times.

Would be curious to find out what that means, since I see it repeated many times in favor of mainstream and verbose languages, such as Java. I get it to mean somehow that Java is more readable, however in my experience that doesn't make sense, as the IDE can deal with generating the boilerplate, but the IDE cannot help with readability. Not even if you include quick navigation by click, since that has been the purpose of ctags in plain editors.

It means that anyone strange to the code is able to pick a pile of printouts and have a ruff understanding of the code.

Which is very hard in languages that are a pile of hieroglyphics.

This is very important in teams of 50+ developers, scattered around countries with high attrition rate, having various skill levels.

Also generating boilerplate is just 1% of what an IDE is capable of.

The mathematical language is a "pile of hieroglyphics", yet mathematicians can speak it just fine. And in fact without their domain specific language mathematics would be much, much harder to deal with. We know this because the mathematical language in use today is newer than the discipline of mathematics and we can make a before and after comparison.

Personally I don't understand why some programmers insist on communicating only in english words. First of all because english words suffer from the problems of natural language, which is that natural language is imprecise, with the words having multiple meanings depending on surrounding context. For example when adding two numbers, should you use "add" or should you use "plus"? As it may be, "add" is actually incorrect according to its precise English definition, because the operation doesn't necessarily lead to increasing the size or amount. Yet this does not stop people from using it. Fortunately classes like BigDecimal are using "plus", yet I don't get why in the world would anybody think that "x.plus(y.multiply(z))" is more readable than "x + y * z".

You might thing picking on BigDecimal is a cheap shot. Well, how about the well known "ListUtils.union(a, b)" and why would that be better than "a ++ b". Speaking of "ListUtils.union", joining 2 lists is not a union, as union is in the context of sets or maps. Joining 2 lists is a concatenation and that matters, because concatenation is not commutative, whereas a union (of 2 sets or maps) is commutative. Basically the Apache Commons folks have got the naming wrong and if such mistakes happen in libraries that are so public, guess what happens inside corporate projects.

But much more problematic in languages like Java is not the wording as much as the way the logic is often expressed. In a functional programming language you usually get pure expressions that operate on immutable data-structures, pure as in true mathematical functions (for the same input you always get the same output). Such functions are much easier to reason about and much easier to test, because the output does not depend on some object's history (or in other words, an object's identity).

The worst and most unreadable code I've ever seen was written in Java, a language in which people often pass around mutable data-structures, like arrays, modifying them on the spot, for no good reason other than not knowing any better, in a dance of mutation that can only be considered an abomination of nature, with code so obtuse that it would make grown men cry. It's not uncommon for pieces of code to be commented with "here be dragons" with people no longer understanding what it does. Couple that with "enterprise design patterns" based primarily on IoC containers and best practices spawned from hell, with deep layers of inheritance that don't make sense and with chronic multi-threading issues and you've got a recipe for disaster. And you don't even have to search for proof for too long. Take any reasonably sized open-source project and you'll see this fact in all its glory. The last Java open-source project I interacted with is SpyMemcached and is a fine example of a Java project that works, that does its job well, that is reasonably well maintained, but that has internals that expose all the problems that I just mentioned. And sometimes I wonder why anything written in Java works at all, my guess being simply that people hit that code with the hammer until it quacks, in a process that resembles more natural evolution and mutation rather than engineering.

Therefore I'm personally dumbfounded by claims of readability. And in regards to the capabilities of an IDE, I do use IntelliJ IDEA, but I am wondering what the other 99% of its capabilities are, preferably that help with readability. Syntax highlighting?

Eclipse folds the import section, and you can get the type of a value by hovering over it, but apart from that, I'm not sure.

The verbosity not just directly affects readability by simply being longer and more to read. It also affects writeability which in combination with lazy developers (as we all are) leads to bad, unreadable, code. This is true for any language and can most often be seen on sloppy error handling, if you have to write 2x the amount of code to handle errors will you do it? 10x?

Odd that this is showing up as a jab against Java and not, say, Go.

Oh, it is very much relevant for Go, except that those of us interested in expressivity simply gave up on Go a long time ago.

You see, Go is not popular enough to have users that don't like it. The people that end up trying out Go simply move to something else if they don't like it. Go's community is also strongly opinionated and has rejected any dialog for meaningful improvements. In other words the Go community is filtering out people that want something different. Whether this is good or bad, you be the judge, but if there's one thing that's definitely bad is the echo chamber. Case in point you're under the impression that Go is tolerable, even though many of us consider it to be worse than Java.

You misunderstand me. I find Go painfully verbose, mostly because of the elaborate if/else constructs around every single method call to handle errors. Even Java's ill-conceived checked exceptions (which can be suppressed with lombok's @SneakyThrows) are a massive improvement.

Completely agree. This is really the best time for comments in Java. Have 5 lines of stuff that's essentially working towards a single goal? Comment it! You should end up with the comments reading out the narrative properly, even if the code is way too wordy.

Java's verbosity (and misuse of OOP, but to be fair, that's not the fault of the language) can get in the way of understanding. Why use multiple words to say something when a single word will do? "Unreadable" is obviously hyperbole; "harder to read" is probably more accurate.

On the contrary, I think the recent additions from Java 8 are a step in the right direction.

> more (idiomatic) libraries and tools

Except almost all the tools work with the pre-java-8 way of doing things (and often even pre-java-7). Thus, you end up with layers of different idioms, all of which are not quite compatible with each other.

Disclaimer: I personally prefer clojure, but earn most of my money as a consultant / coach helping clients develop Java code.

For Java code, it's often easier to predict the performance characteristics of a part of the code. But you shouldn't trust your judgements anyway - instead use a profiler - so this is not really a reason.

Speaking of performance: I once talked to someone working for Elasticsearch, and they told me that they cannot use clojure for most of their code because of their performance requirements. Clojure collections are almost as fast as Java collections in many scenarios, but that is apparently not fast enough for them. This is probably not a valid reason for most projects, though.

There is a huge number of Java developers out there, and Java is easier to learn for someone coming from C++ or C# or JavaScript than clojure (at least I am pretty sure about that). So hiring cheap developers might be easier if you use Java. You might count that one as a political statement.

There are some great tools for Java out there: IntelliJ Idea, Yourkit Profiler, JRebel, ... Cursive Clojure is great, but IntelliJ for Java has even more integrated workflows.

There are very mature open source libraries / frameworks / tools for Java: JUnit, Spring, Guice, Dropwizard, Gradle, Elasticsearch, ... You can have commercial support for many of them. Most of the libraries I use with clojure are version 0.3 or something like that with no possibility for commercial support (but they work fine anyway).

Static typing: A lot of people complain about it, but it can really help you to create maintainable code. See for example http://talks.samirtalwar.com/use-your-type-system.html

Really high performance Java code tries to avoid any allocations on the hot paths, since that requires GC. This means a lot of in-place mutation on pre-allocated objects. (Lucene does this, for example) While this isn't impossible in Clojure, you do have to give up a lot of the immutable goodness that makes it such a pleasant development environment.

All IME, and as a Scala fan:

Clojure has a weakly integrated type system (an inherent disadvantage of optional type systems). At the simplest level this makes silly errors much easier and means you have to write more tests to maintain the same defect rate.

The lack of types mean you require extensive use of macros for advanced functionality. IME macros have major maintainability issues in a multi-person codebase.

Both these things are major disadvantages for automatic comprehensibility of code. Autocompletion can be more-or-less usable but will never be as good as in Java or Scala. Automated refactoring is inherently unsafe in the presence of macros (Scala's fancier typed constructs (typeclasses, for/yield with custom types) mean you need macros much less often; Java tends to force you to expand these things out by hand (or else use annotations which act as de facto macros), which has its own maintainability issues but does at least mean automated refactoring will work correctly).

Some of the language culture pushes people towards less principled abstractions. From this side of the fence that looks like anti-intellectualism; no doubt from their side it's pretension on typed programmers' part. But either way I think they're setting themselves up for long-term maintainability issues (e.g. the semantics of clojure transducers in the presence of errors are infuriatingly not-quite-right, which will either remain a painful gotcha forever, or necessitate a painful migration in the future).

As a minority language Clojure may not be as well supported in the surrounding ecosystem - partly things like IDEs but also code coverage tools, profilers, monitoring.... Remember the JVM ecosystem is wider than just the languages themselves.

There are good things about Clojure, but it's by no means clear-cut.

A while ago I decided to teach myself how the common machine learning algorithms worked and, to make things interesting, decided to implement them in parallel in both Clojure and Scala. I expected Clojure to be a better fit for this since Lisp has historically been strong in AI. But I found as my codebase grew that the Scala code was much easier to maintain, refactor and understand. So these days I am a big fan of static typing provided it's in the form of a sufficiently expressive type system (Scala, Ocaml, Haskell, Swift etc).

Lisp got a lot of things right but I'm not sure dynamic typing and s-expression syntax were among them.

> IME macros have major maintainability issues in a multi-person codebase.

I have heard this a bunch, and I believe it, but I've never personally had an opportunity to use a language that supports macros for a multi-person codebase. I'd be interested to learn what are some of the pitfalls you've seen, if you don't mind sharing.

It's not like I have a lot of experience (macros are much rarer in my primary language, Scala). But it's usually just someone doing a refactor that "makes sense" according to the logic of the regular language but not within the macro. A lot of it is simple, "silly" things like trying to extract a repeated piece of logic into a method call, only it doesn't work because the logic uses something the macro hooks into, or just is transformed in a surprising way by the macro that makes the reasoning incorrect. To take a completely trivial real-world example, renaming a field is usually a safe operation that you can do automatically (with an IDE) without even testing, but if that field's used in a macro then maybe it represents a key in a JSON object and you've just broken your web API. You notice it pretty quickly, and it's a two-minute fix - but these kind of "thousand cuts" scenarios really slow development. Outright production failures are less common but I've seen those when test coverage was poor or other failures interacted.

What do you think is not quite right about errors in tranducers? When you use them with core.async channels you can supply an optional exception handler, which is quite nice.

In terms IDEs, Cursive for IntelliJ is great, and it gives almost Java-like capabilities (with limitations inherent to more dynamic languages, of course).

I forget the exact behaviour, and maybe it's been improved. But I remember that it didn't map cleanly onto values - sometimes you want to continue after a single error, sometimes you want to bail out immediately, and the ways of doing this felt very ad-hoc.

I think you can do what you are describing with a channel https://clojure.github.io/core.async/#clojure.core.async/cha...

> The lack of types mean you require extensive use of macros for advanced functionality.

Could you elaborate?

A lot of things that can be implemented natively in scala using its fancy type system would likely be done with macros in clojure. E.g. scala-arm, treelog, dependency injection (not that I use it), database transaction management. Or even just async.

Java works great on large teams. The stuff that people complain about as "too much boilerplate" and "super boring" when you're writing code is actually really awesome when you have to read a bunch of code and half the people who wrote it are gone. Boring is better than clever when it comes to maintenance.

They're conservative about adding features, so they've managed to keep the language pretty small, and the core concepts of "what is good Java code" have been mostly the same for like 15 years, even through major releases like 1.5.

I don't really find it "actually really awesome" when I have to read through 6 screens worth of getters and setters in legacy Java code vs what it would have looked like in C# or Scala. In Scala or C# you're looking at the actual logic, not playing hide and seek with it amongst irrelevant boilerplate in Java.

Java developers usually consider features which the language doesn't have to be bad or make code unreadable, that is until those same features are added to Java, when suddenly they are a source of pride and a sign of Java being "modern". In reality Java's feature freeze until Java 8 was largely driven by Sun's financial woes.

I think jbooth is talking more about forced verbosity such as

  Map<String, Customer> customerMap = createCustomerMap();
where in other less verbose languages you find

  val customerMap = createCustomerMap();
Always seeing the definition of a value in the current function context is actually very nice for maintainability, but does give Java the reputation of being overly verbose.

Stuff like getters/setters on all values in a class are awful though and are both a code smell and unnecessary. They break the OO principles and should not be there in the first place. It's good that Java makes those 6 screens worth of getters and setters awful - they are awful. For objects which are used solely for transfering state, check out Google's AutoValue ( https://github.com/google/auto/tree/master/value )

However if you do see a class that is 6 screens of just getters and setters then you know exactly which class you need to fix.

I agree with you, up to a point. I find Ruby code pretty awful to make sense of with very little type information. Type inference with explicit return types for methods strikes a nice balance between verbosity and readability. I think Scala, Kotlin and to some extent C# do a better job of this than Java.

There are also many less favourable cases where you see things like:

   Customer customer = new Customer();
   String str = "I'm a String"
   SomethingOrOtherFactory factory = new SomethingOrOtherFactory();
which isn't any clearer than:

   val customer = new Customer();
   val str = "I'm a String"
   val factory = new SomethingOrOtherFactory()

Having a dozen getter/setters is generally a code smell, but having a dozen getters isn't.

Agreed, but:

  Animal customer = new Cat();
It makes it clear how the new object will be used. It does come up quite frequently, especially with Collection and List. Nobody was saying it is not verbose and can lead to boilerplate. The assertion is that when viewing a large code base, you are always sure of the type of value you are dealing with on a local level. If all of your objects are well defined, you don't even need to look at how the class is created (a billion getters and setters? irrelevant to the local function) as you only need to deal with the local function.

That's there if you want it though.

    val customer = new Cat(): Animal
I can see arguments against inter-method type inference, but I think there's no real argument against type inference within a single method (requiring explicit types on method boundaries). If you have a billion-line method you have bigger problems.

The first time I saw properties was in Eiffel, afterwards adopted by Delphi, which eventually found their way into C# and to many other languages.

Comparing to the original implementation of properties in C# (not how they look now), the Java way wasn't that different.

They just copied what was common C++ and Smalltalk practice back then, whereas Anders obviously had his Delphi experience.

Not that they shouldn't eventually improve it.

IMO Java doesn't shine anywhere. It's statically typed, but lacks support to be flexible. Thus many Java programs rely on reflection at runtime or casting. This negates all of the benefits of static typing, turning the language into a dynamic one.

The main choice is then either Clojure or Scala. This comes down to dynamic versus static type checking. With a dynamic language, one is significantly less sure if a program is correct. In large programs that use dynamic languages, many tests must be created that are essentially doing the work that a type checker in a static language would do automatically.

In a static language, entire classes of bugs are eliminated. Scala provides the best support for strong, static typing on the JVM.

> Scala provides the best support for strong, static typing on the JVM

I'd argue that Ceylon and Kotlin offer most of Scala's advantages with none of its baggage and bloat. And drama.

All those things will come as those languages mature, and until then neither has much library or tool support.

No tool support?

Kotlin and Ceylon both have very good IDE plug-ins that already work much better than Scala, despite Typesafe pouring a lot of money into their own plug-in.

There are a lot more tools in the JVM ecosystem than just IDEs, and by now most of them have Scala support.

I love clojure, but on a large code base I find good refactoring tools to be extremely helpful when trying to evolve how things are implemented. In that respect Java wins hands down just because of the tooling around it. It also may be easier sometimes to use Java for working with particular libraries.

There are however areas where I'd use clojure every time. Luckily the JVM is really good for doing mixed language work on, so you don't have to work entirely in one language.

A type system (at least scala)? Although Clojure does have one, it's not as good to use as Scala's (personal opinion).

In the same way some people pick C++ over Lisp, some would choose Java over Clojure for similar reasons.

It's not some kind of war or personal affront to you - some people simply prefer the Algol style to the Lisp one. This even includes people who are very fluent in the Lisp style.

There is no non-political reason to enumerate choices of languages limited to those that run on the JVM.

Clojure and Scala exist because of the JVM, not because of Java. Java 8 will or won't be an interesting language independently of the JVM, oddly enough.

It is the systems programming language of the Java platform, if you see the JVM as an OS.

So you need to be comfortable with it, as there will always be cases you need to jump out of the alternative language into Java.

Also, most companies will only hire to work with Java on the JVM, because they don't want to be hostage of the cool language some of the dev team guys/contractors decided to use instead.

It is very rare to see traditional Java shops to use alternative languages.

Usually the ones using them are either startups or companies like Facebook and Twitter, which aren't the typical business culture.

I have been using Java since the first beta, and have written a large pile of Java books (pun not intended), but I too prefer Clojure as a practical, simple to use and simple to read language. That said, Clojure / Java interop is really nice. I frequently find myself simply adding existing Java code to my lein project file.

I would suggest just keep using Clojure and when it makes sense, mix in Java when you want or need to.

This might sound like a snarky comment but it is actually not.

If you want a dynamic language on the Java platform; then why not use JavaScript?

There is an easy-to-use engine in Java 8 and JavaScript is widespread and somehow familiar to Java-developers.

JavaScript is just not a nice language to maintain - bizarre scoping, confusing inheritance model, extremely clunky syntax, confusing standard data types. Being "dynamic" isn't a feature so much as an acceptable cost, but JavaScript doesn't really give me any expressibility advantage over straight Java, yet alone compared to Scala.

I like JavaScript for Meteor.js and general front end development but I have never thought to use embedded JavaScript. Clojure is a practical little language and suits my taste.

Most parts of software development probably is not about starting new projects from scratch but about maintaining existing code bases. That's where Java really shines due to its (often hated) backwards compatibility.

Apart from the slightly better performance, memory consumption. Clojure's persistent collections require a fair amount of memory (which in most cases doesn't matter as memory is cheap).

Java shines by the number of java professionals available on the market.

Many people have a gut-level aversion to dynamically typed languages.

> Is there a non-political reason that someone should choose Java over Clojure (or possibly Scala)? Where does Java shine?

If you are stuck with a group of developers who have no interest in learning anything new and wish to use Java until they retire then Java is the only choice. Other than no new learning required I can't think of many reasons to pick Java as a first choice. Java tends to be popular because it's the oldest, most familiar default for the JVM rather than any particular strength.

>If you are stuck with a group of developers who have no interest in learning anything new and wish to use Java until they retire then Java is the only choice.

Put another way: if you're on a dev team where nobody knows Clojure/Scala, and everyone knows Java, (and, optionally, the codebase is already in Java), and your team doesn't have time for everyone to learn an entirely new language (including cleaning up the beginner's mistakes that come with that process), then Java's the better choice.

Don't get me wrong, Java grinds my gears sometimes, and I frequently wish I could use Kotlin or F# or some kind of typed Ruby in my Java-shop workplace, and I enjoy picking up the cool ideas from languages like Idris (dependent types are really cool!) in my free time. But I'm sympathetic to my coworkers in the environment where I work: 4 devs in the whole company, startup pressure to get to a break-even point so we don't go under, and a backlog longer than all of us could finish this year even if we froze it now.

Sometimes circumstances require what looks like a short-term decision in order to ensure that there's a long term to worry about later.

You can write Java-without-semicolons in Scala and be productive from day 1, possibly even hour 1. Then you work up to fancier features as and when they provide a practical advantage. That's how I started.

With existing Java code and/or a tight timeline with existing skills I can see how that would be the best choice :) I'm thinking more in terms of the surprisingly high proportion of Java developers who have never learned another language since learning Java 10-15 years ago and dismiss any alternatives without ever exploring what else is out there (you clearly don't fall into that category :).

This is quite typical in most companies where devs don't have trainings nor wish to spend time with work in their free time.

I bet you will find thousands of C++ devs that don't even know that there are newer versions of C++98.

C devs that still use K&R as their guide with C89 code style.

And so on.

We are the few select ones that care to improve our skills.

EDIT: Typos.

> I bet you will find thousands of C++ devs that don't even know that there are newer versions of C++98.

Actually had a guy tell me (in 2006!) that the main problem with C++ was that there was no standard. :|

There's an irony that your comment is in a thread about learning new features of Java 8

Java EE is currently an excellent framework for complex distributed enterprise applications.

It wasn't touched on in this document, but I think more people should hear about Nashorn. It is the new JavaScript Engine that, I believe, supports ES 5.1. It allows you to do some pretty neat things with Java/JavaScript interacting in the same VM. Java 9 is planning on supporting ES6 <source needed>.

You can see an introduction on this Oracle documentation: https://docs.oracle.com/javase/8/docs/technotes/guides/scrip...

I am the only one that has been thinking for months now that "modern" is the most over used, pointless, and almost negatively connotative word in this industry? Thought exercise: Imagine the ugliest flat and incomprehensible UI widget you have seen lately, the most bloated website load that doesn't crash the browser, and some mindless office dialog about "rising cloud costs". Now pick one word. Survey says - you guessed it. So most certainly, if you need to be more "modern", I'm sure java 8 is a vehicle to take you there.

My company switched from 7 to 8 recently, and being able to use streams has been a colossal productivity boost.

I'm actually in the process of putting together a presentation on how to work magic with streams (because I'm one of the few people here who actually knows how to use them), and this will probably get a mention in a "further reading" slide, because it's really well written.

It's also inspired me to make a few slides focusing on the changes to Map and Comparator, which I wasn't aware of until now.

A really nice tutorial.

I have been writing Java code since 6 years. Although I can't say that I have programmed in many other languages (I have coded in C/C++ and Javascript), Java has been always very comfortable to use. It's easy to think of a solution in the Object Oriented paradigm, and I think refactoring (when a requirement changes) has not been much of a problem for me. It's only bad designs that make the code more verbose than it should be, and I think verbosity is actually a good way to articulate your thoughts, and see them evolve in front of you. Recently when I started learning Haskell a bit, I found that there are much faster and concise ways of writing code, but I still prefer Java because of the clarity of the way it allows me to express my logic. People would say that writing a line of code in Haskell would do what 10 lines of code in Java would do, but in the long run, I have found that expressing programming paraphernalia like interfaces and classes actually helps the coder in his job (not necessarily results in wastage of time).

For people getting into Java 8, jOOL is a great, small library with a "Seq" class that fills in the gaps missing in j.u.stream.Stream:


E.g. if you're coming from groovy/Scala and wonder "why doesn't Stream have this method?", Seq probably either already has it, or the maintainer will be open to adding it for you.

So, use jOOL. It's great.

That said, I still cringe that we have to do "someCollection.stream()..." (or use fill-ins like jOOL) at all, because these methods aren't on j.u.List/etc. itself...

My naive impression is that the JDK designers fixated on making streams support parallelization (because fancy!), and so made some compromises on the API, when in reality 98% of collections are small/not parallel, and I assert a non-parallel, more complete API (e.g. more default methods directly on j.u.List/j.u.Iterable themselves) would have been a net-win to most programmers.

And the parallelized version could be a separate library/jar/something.

That library has some fun/interesting generics declarations like this one (https://github.com/jOOQ/jOOL/blob/master/src/main/java/org/j...)

> static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> Seq<Tuple16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>> crossJoin(...

Pretty much all languages with generics will have that kind of declarations, the only question is how many type parameters they'll go up to. In C# Action goes up to 16[0] while Rust's tuple go to 12[1] and GHC goes to 62[2] (intending 100, but a comment notes declaration of a 63-wide tuple constructor segfaults)

[0] https://msdn.microsoft.com/en-us/library/dd402872(v=vs.110)....

[1] https://github.com/rust-lang/rust/blob/master/src/libcore/tu...

[2] https://downloads.haskell.org/~ghc/7.2.2/docs/html/libraries...

Yes, the only way to avoid this is to do as C++ and D, accept variable number of types in the generics.

That's an option, but it requires a significantly more complex generics runtime.

interfaces with default implementations? Doesn't this look like... multiple inheritance?

You can still not have member variables, so the default implementations can only be implemented in terms of other methods. So it should probably be classified as 'limited multiple inheritance'. It does have the diamond problem though.

That was my immediate thought - this is closer to multiple inheritance, as it introduces the diamond problem: https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamo... According to that page, Java forces programmers to resolve the ambiguity by making it a compiler error. A reasonable approach, I think.

I've done more OO development in C++, and I've found this technique useful - putting a default implementation in a class which was mostly an interface because only a small number of subclasses needed specialized behavior.

More like traits. Multiple inheritance of implementation is problematic because it allows parent classes to define state. Traits and Java 8's interfaces don't.

they only add behavior based on other already-present methods, they don't add fields.

Right, not adding fields makes all the difference.

My exact thought.

so... when is this coming to android?

Support for java 8 will come with jack & jill. It might also need a new API level (24 ? ), which would seriously hamper its adoption. This is why many people see kotlin as a great thing for the future of Android (even though kotlin on android could also benefit from java 8 as a target)

You can use a good number of these today with Retrolambda: https://github.com/orfjackal/retrolambda

Really like quite a few Lambda stuff in Java8, sadly once you need to touch any of the intrinsic types it gets ugly real fast. Wanted to do some byte manipulation with Lambda and I'd be creating a new Byte object for each byte I touched.

Where is aysnc await?

CompleteableFuture[1] is the closest we got for now in the JDK.

[1] https://docs.oracle.com/javase/8/docs/api/java/util/concurre...

I just started using CompletableFuture last week. The way each method ties your lambdas together without a bunch of type boilerplate is really nice. I still don't have a great handle on all of its methods, though; e.g., what's the difference between get() and join()?

CompletableFuture implements Future, so the get method is an implementation of Future.get [1].

Anyway, the major difference is that Future.get throws 2 checked exceptions, InterruptedException and ExecutionException, while CompletableFuture.join does not throw any checked exception. Instead, it wraps any exceptions in CompletionException.

[1] https://docs.oracle.com/javase/7/docs/api/java/util/concurre...

Gosh... java is getting uglier by these weird syntax.

> Default methods cannot be accessed from within lambda expressions.

Why is that?

If I had to guess, it's because the lambda will be expanded at runtime to an anonymous interface implementation. Then either the runtime part is the problem or maybe they use a different, but compatible functional interface for the ad-hoc implementation, which then has no access to default methods on the one you're assigning to.

That is not how lambdas work actually work in the reference JVM.

They make use of invokedynamic, there is no expansion taking place as many think.


The changes are breathtaking. It doesn't feel like Java.

Sorry, I absolutely cannot stand this "modern" meme.

  (define-syntax stream-cons
      (syntax-rules ()
          ((_ x xs)
              (cons x (delay xs)))))

  (define (stream-cdr xs)
      (force (cdr xs)))

  (define (stream-map f xs)
      (stream-cons (f (car xs)) (stream-map f (stream-cdr xs))))

  (define (stream-filter f xs)
      (if (f (car xs))
          (stream-cons (car xs) (stream-filter f (stream-cdr xs)))
          (stream-filter f (stream-cdr xs))))

  (define (stream-take n xs)
      (if (<= n 0)
          (cons (car xs) (stream-take (- n 1) (stream-cdr xs)))))

  (define (repeat x)
      (stream-cons x (repeat x)))

  (define (iterate f x)
      (stream-cons x (iterate f (f x))))

  (define (replicate n x)
      (stream-take n (repeat x)))

  (stream-take 5 (stream-filter odd?
      (stream-map (lambda (x) (* x x)) (iterate (lambda (x) (+ x 1)) 1))))
Not mentioning all these different flavors of Common Lisp streams packages.

As for other features, Scala is around for almost a decade.

It may not be modern computer science, but it is modern Java. This post is about modern Java and explains the new (in v8) features very well. What's your problem?

OK, what then is Java < 8?)

The point was that if one has a proper old-school computer science (to realize the crucial importance of first class procedures and the power of uniformity 20 years ago) all these modern features are coming for free, to which the code above is an illustration.

But who cares. There is a whole industry which praised Java for almost two decades even without these "modern features".

Any PL student of a decent school, if he is not a hypocrite, would tell you that Java is the worst thing that happened to CS since MS DOS, but who cares about PL theory or even CS? Availability bias and Cargo Cult is enough.

> Any PL student of a decent school, if he is not a hypocrite, would tell you that Java is the worst thing that happened to CS since MS DOS,

That's why we don't listen to students to make this kind of uninformed claims.

Java has flaws, for sure, but calling it the "worst thing since MS DOS" can only come from someone who lacks perspective and experience.

Why, I have experience of re-instalation and restoring data from backup each time Business Objects JVM process segfaults, because there is no way keep track of state unless it shuts down property. How about this perspective?

Good for you but it doesn't make your previous statement any less of a preposterous hyperbole.

Tell us, please, what language comes to one's mind instantly afer reading this chapter below?


Yes, and now that Java finally has these modern features: people are migrating to GO who lack many :(

But then LISPs have always been shunned for bad reasons. While crazy bad languages like C,Java and PHP are picked up by the masses.

Your JVM runs on all things written in C and the concurrency primitives are far more elegant in Go than in Java.

Agree and disagree. The thing I was going for is that compscience advantages don't seem to matter for language adoption. i.e. popularity does not correlate to compsci purity.

Yes, there are different forces in a play.

Let's name it as The Law of a Decent Runtime, and The Law of Attention to Details, and The Law of a Bazar of Ignorant.))

The Law Of a Decent Runtime is very simple - evolving a decent runtime is very costly and time-consuming. It is also related to the second law and to the inverse of the third - which is a the Law of Dictature of The Most Competent. A decent runtime cannot be produced without talent, time, financing, competence and attention to details.

The examples are what came out of Xerox Parc, Bell labs, Ericsson and the best parts of academia (MIT Scheme culture, Scala, Standard ML, LLVM and Haskell, monads aside).

There are also many in-house (Tensorflow) and sponsored open source porjects (LLVM, Golang, Julia, Torch, to name a few). Basically, it is about resources spent on a talent.

What is important distinction - a decent runtime cannot be produced by the Bazaar of Ignorant (PHP, amateur Java code, SAP, and other "fractals of a bad design").

The Linux kernel is very special example, because it combines the law of big numbers, and all these three - it is a product of a whole planet of competent volunteers and paid developers - unlike PHP or Java ecosystem there is very high barrier to entry, thanks to The Dictature of The Most Competent .)

The law of Attention To Details - is quite obvious, and related to the first. This is why most of successful projects had a passionate and competent leader who sets the standards, be it Linux kernel, Erlang, Nginx, Gambit Scheme, Python, OpenBSD, Redis, Scala, SBCL, PostgreSQL, you name it.

Popularity has nothing to do with it. It is based on the principle of instant gratification (PHP, MongoDB) or Availability Bias boosted by paid content brainwashing (Java, SAP, MS) without understanding and preferably any thinking. Popularity doesn't mean quality at all, be it junkfood or PHP.

Your Law of a Decent Runtime reminds me exactly of the "Lisp Curse":


But there are really modern things. Julia, for example, and Scala is even a bit too modern.)

Enjoy these features while you can.

It's a matter of time until your team lead takes a glance at the code-base, deems them unreadable and "too clever" (as in "where am I going to find cheap developers now?") and makes you revert all the way back to Java 6.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact