
New Features in Java 14 - pjmlp
https://blogs.oracle.com/javamagazine/java-14-arrives-with-a-host-of-new-features
======
rafaelvasco
"Helpful NullPointerExceptions": One of the biggest pain points of Java,
probably the biggest. Good that it's being finally solved;

~~~
cryptos
Java is quite far away from finally solving NullPointerException issues.
Kotlin, C# or TypeScript with their compile time null checks solved it mostly.
Common to these three languages is that they need null to be interoperable
with older language versions (C#) or with related languages (Java,
JavaScript). Languages like Rust that don't even know null (or nil as in Go),
have finally solved the problem.

~~~
rakoo
> Languages like Rust that don't even know null (or nil as in Go), have
> finally solved the problem.

I'm in no way a Rust expert, but I see a lot of code with Optionals, something
like

    
    
        match sth {
          Some(v) => do_something
          None => nothing_to_do
        }
    

This looks awfully a lot like

    
    
        if (something == null) {
           nothing_to_do
        } else {
           do_something
        }
    

It might look more pleasant, but it doesn't "solve" anything, only shifts it
in a different place.

Go is in the middle, because structs have no null value, only a zero value;
the only thing that can be nil are pointers which I personally feel should
used as little as possible.

~~~
Sharlin
In Java you're passed a reference to an object. Might it be null? _Can_ it be
null? Who knows! Mostly you're just going to ignore the possibility and hope
for the best.

In Rust you're passed a reference to an object. Can it be null? No, it simply
can't! There does not exist a magic "null" value for references. To represent
the possible absence of a value, you _explicitly_ encode it into the type
system by using `Option<T>`.

~~~
hcarvalhoalves
Just changed from having a bottom null type for all types to everything being
a boxed Option type. It’s equivalent.

The actual difference is the reliance on pattern matching and the compiler
enforcing coverage on those languages.

~~~
LessDmesg
Option types don't have to be boxed and are specifically optimized in Rust
and, if I remember correctly, in Haskell. The Nothing is represented much like
a null pointer, i.e. with a special value that can't be dereferenced.

~~~
tome
Not in Haskell, FYI. Maybes are boxed.

------
mateuszf
I wonder about records - they would be great for simplified implementation of
immutability, but they seem to not provide a way to copy with a subset of
modified fields - like in Scala:

    
    
      case class Person (
        firstName: String,
        lastName: String,
        age: Int
      )
    

you could create an instance like this:

    
    
      val emily1 = Person("Emily", "Maness", 25)
    

and then create a new instance by updating several parameters at once, like
this:

    
    
      // emily is married, and a year older
      val emily2 = emily1.copy(lastName = "Wells", age = 26)

~~~
Benjammer
You'd probably have to manually write a Builder in the Person record class.
Then the callsite might look something like this:

    
    
        val emily2 = emily1.newBuilder()
                        .lastName("Wells")
                        .age(26)
                        .build()
    
    

I think the inner Builder pattern is common enough in immutable java object
implementations that they might want to include that fully in the Record class
generation at some point. Most immutable object libraries that I've seen
include a lot more functionality than Records, like builders.

~~~
jeswin
Why not do what JS does?

    
    
      val emily2 = { ...emily1, age: 30 };
    

This has many advantages, one being that it provably and declaratively creates
a copy, which is not the case with the builder. In fact the obsession with
methods (and hence the implied state) is what makes Java a terrible misfit for
functional paradigms such as immutability.

~~~
sa46
I don't understand what you mean by provably. The Java example does the exact
same thing as the JS version: create a shallow clone with a different age. Do
you mean the builder might do something different?

    
    
        // Java
        val emily2 = emily1.toBuilder().age(30).build()
    

As far as adding the spread operator to Java, I think you'd have to require a
Spreadable interface to use it or limit usage of the spread operators to
records. It's not clear to me that the expressive power is worth it over a
builder.

~~~
jeswin
If a tool analyzed the JS AST, it could conclude (with fairly trivial
analysis) that emily2 is a shallow clone of emily1. No such guarantee is
possible with the Java version.

------
redact207
"switch expressions", "text blocks". I haven't used Java for 15 years but it's
one of the most mature languages out there. I'm surprised it's only just
getting these pretty standard features now.

~~~
dionian
it's always moved slow on purpose... one of the language's greatest features
is backwards compatibility and stability

it's purposefully dead simple. this is why many of us switched to alternative
JVM langs ... Java can't keep up with the innovation other Langs have without
breaking its philosophy of slowmoving/backwards compat

~~~
jfim
To be fair, it moved much faster after the Oracle acquisition.

The Java 6 era of no changes whatsoever was slow as molasses. The joke back
then was that even C++ got lambdas before Java.

~~~
chaorace
Anonymous inner classes sucked royally. Java 7 rocked my world when it
dropped!

------
leibnitz27
negative instanceof is a _disaster_

I posted a bit about it here
[http://www.benf.org/other/cfr/java14instanceof_pattern.html](http://www.benf.org/other/cfr/java14instanceof_pattern.html)

it was first noted
[https://twitter.com/tagir_valeev/status/1210431331332689920](https://twitter.com/tagir_valeev/status/1210431331332689920)
here

but the main thing is that if the 'taken' conditional is guaranteed to exit,
then scope hiding happens, if not, not.

But in java

if (true) { throw new Exception(); }

is not guaranteed to exit.

So:
[https://github.com/leibnitz27/cfr_tests/blob/master/src_14/o...](https://github.com/leibnitz27/cfr_tests/blob/master/src_14/org/benf/cfr/tests/InstanceOfPatternTest10.java)

In case you don't want to run, as of java (build 14-ea+34-1452) this prints:

Fred

WIBBLE

    
    
      public class InstanceOfPatternTest10 {
        static String s = "WIBBLE";
    
        public static void test(Object obj) {
            if (!(obj instanceof String s)) {
                throw new IllegalStateException();
            }
            System.out.println(s);
        }
    
        public static void test2(Object obj) {
            if (!(obj instanceof String s)) {
                if(true) {
                    throw new IllegalStateException();
                }
            }
            System.out.println(s);
        }
    
        public static void main(String ... args) {
            test("Fred");
            test2("Fred");
        }
    }

~~~
vbezhenar
Looks like a bug. But anyway should be easily detectable by a static analysis
and reported as a warning, so not a big deal in practice, even if working as
intended.

~~~
leibnitz27
Nope - the if condition is not considered in flow analysis. Read the end:

[https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.htm...](https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.21)

"14.21. Unreachable Statements It is a compile-time error if a statement
cannot be executed because it is unreachable.

This section is devoted to a precise explanation of the word "reachable." The
idea is that there must be some possible execution path from the beginning of
the constructor, method, instance initializer, or static initializer that
contains the statement to the statement itself. The analysis takes into
account the structure of statements. Except for the special treatment of
while, do, and for statements whose condition expression has the constant
value true, the values of expressions are not taken into account in the flow
analysis."

~~~
vbezhenar
I still don't understand why reachable or unreachable changes the binding from
names to local variable/field. The process of identifier resolution should
happen before reachability analysis.

Is there some bug or mail list thread with reaction from Java developers?

~~~
leibnitz27
I haven't raised mine, as I consider it to be a refinement of the bug noted in
[https://twitter.com/tagir_valeev/status/1210431331332689920](https://twitter.com/tagir_valeev/status/1210431331332689920)
(Don't know if java devs have responsed to that.)

(again, reachability analysis of unrelated code changes semantics.)

The problem is that this IS defined behaviour - the scope of the instanceof-
assigned variable is dependent on whether or not the taken if-statement is
provably exiting.

This is intended to allow

    
    
      {
        if (!(obj instanceof String s)) return;
    
        // s exists now.
      }
    

But it's not been thought through.

~~~
vbezhenar
IMO they should treat it exactly like they treat uninitialized variables.

    
    
        void test1(boolean b) {
            String s;
            if (b) {
                return;
            } else {
                s = "test";
            }
            System.out.println(s);
        }
    

This code compiles.

    
    
        void test2(boolean b) {
            String s;
            if (b) {
                if ("a".equals("a")) {
                    return;
                }
            } else {
                s = "test";
            }
            System.out.println(s);
        }
    

This code does not compile. But it does not mean that println will try to
resolve s to something else. I think that they should have gone to a similar
route, where declared variable will be available for entire lexical block
where `if` was used, but initialized only inside matched branch. Usage in
other code would error with "variable might not have initialized" consistently
how it works now.

Of course that would require to shadow previous declaration for consecutive
`if`-s. But it would be much more obvious and understandable. Actually the
whole construction would be just a syntax sugar almost expressible with
current Java constructions:

    
    
            /*
            if (o instanceof String s) {
                System.out.println(s.length());
            }
            //System.out.println(s.length()); // variable might not have initialized
            if (o instanceof Number s) {
                System.out.println(s.intValue());
            }
             */
            String s;
            if (o instanceof String) {
                s = (String) o;
                System.out.println(s.length());
            }
            //System.out.println(s.length()); // variable might not have initialized
            Number s$; // no variable shadowing in Java now, but it could work
            if (o instanceof Number) {
                s$ = (Number) o;
                System.out.println(s$.intValue());
            }
            //System.out.println(s$.intValue()); // variable might not have initialized
    

and would be directly expressible if Java would allow variable name shadowing
which is a good thing as proven by Go and Rust (although that would be
incompatible change for old code, but allowing variable shadowing for patterns
would not be incompatible change, because old code does not have pattern
variables).

Of course I did not think about this problem for too long and probably missed
something important, so that's just my 2 cents. I guess, developers took that
path for a reason.

Basically they want to following code to work:

    
    
        String s;
        void test(Object o) {
            if (o instanceof String s) {
                System.out.println(s); // local variable o
            } else {
                System.out.println(s); // this.s
            }
        }
    

and I'd argue that this code should not compile! It's bad code. If developer
wants to use `this.s` he should explicitly write that.

------
whateveracct
With switch, records, and (soon?) sealed interfaces, Java will be more
pleasant than ever.

Would I ever choose it if I were in charge of a project? Probably not. But it
is nice that the language is incorporating these proven features that make a
huge difference. It'll make working in Java when I'm not in charge much nicer.

~~~
whateveracct
One question about sealed:

Does anyone know how it will play with type parameters in the interface? I
know in Scala, you can simulate GADTs with that.

    
    
        sealed interface Expr<A> {}
        record IntExpr(Integer i) implements Expr<Integer> { }
        record StringExpr(String i) implements Expr<String> { }
    

Will that be legal? And will switch be able to do its magic and carry that
type variable through?

------
Yhippa
I've been doing mostly Java work in my career and I'm using more JS these days
on the side. I'm envious of the rest, spread, and deconstruction functionality
in ES6. Really, really, envious.

~~~
smt88
What would stop you from switching to Kotlin, which has those features and
many more?

~~~
winrid
I was in a Java shop at one point where leadership said Kotlin would be too
hard for the engineers to learn (they were all mostly fairly junior).

~~~
smt88
Syntax is not that different from Java. Someone who knows Java could be
productive in 1-2 days. But it sounds like you agree that your leadership in
that scenario was not rational.

~~~
winrid
I'm not sure, I never tried Kotlin.

I like the look of it but I know Java pretty well so it's been hard to not
just use that for my personal stuff :p

------
correct_horse
I feel like Java has a particular problem in common with Android - they both
keep getting better, but most people are stuck on some old version.

The difference is that smartphones phones last at most 5 years, but a program
that a business depends on lasts forever.

~~~
vbezhenar
People deliberately chosen to stay with some old version. Java backwards
compatibility is awesome. The only questionable move was with Java 9, when
they removed a lot of classes from standard library and introduced modules,
but even that move was not so hard to migrate.

Some people just don't want to invest ANY money to improve their code. They
just want to release new features. They would use Java 1 on Windows NT 4 if
they could.

~~~
pjmlp
To be fair, many users would do just fine with an Amiga 1000, given what they
do with their computers.

------
namelosw
It's good to see Java is becoming better and iterates faster.

However, I found it's weird that hosted languages like Kotlin, Clojure or
Scala are more approachable for me as they are working fine with JDK 8 like
'libraries'. They're much easier to upgrade than upgrade JDK itself.

------
wereHamster
> Pattern Matching for instanceof

Why is the cast even necessary? Isn't the cast only part of the type checker
and thus unnecessary with a smarter type checker? For example TypeScript can
do it, if you have code following an if condition that checks the type of the
variable, you can use that said variable as if it were of that type without
any further assertions necessary, eg.

    
    
        let x: unknown;
    
        if (typeof x === "string") {
          console.log(x.charCodeAt(0));
        }

~~~
frant-hartm
Two reasons - current convention and backwards compatibility. Java developers
are simply used to the cast, it is commonly occurring pattern (even if it is
from necessity).

The backwards compatibility part:

    
    
      class Foo {
        void foo(Object o) { }
      }
      class Bar extends Foo {
        void foo(Integer i) { }
      }
      ...
      Foo x = ...;
      Integer i = 42;
      if (x instanceof Bar) {
        x.foo(i);
      }
    

Currently this calls Foo.foo, if there was a smart cast it would call Bar.foo,
possibly breaking existing code.

EDIT: Added method parameter.

~~~
koreth1
That's not correct. Java method dispatch is always dynamic, so x.foo() will
always call the foo() method on whatever concrete type x actually is. The
declared type of the variable that holds the reference to that object doesn't
matter, nor does casting (with a couple exceptions, none of which apply here).

But don't trust me, try it!

    
    
        |  Welcome to JShell -- Version 11.0.5
        |  For an introduction type: /help intro
        
        jshell> class Foo { void foo() { System.out.println("Super"); } }
        |  created class Foo
    
        jshell> class Bar extends Foo { void foo() { System.out.println("Sub"); } }
        |  created class Bar
    
        jshell> Foo f = new Bar();
        f ==> Bar@58651fd0
    
        jshell> Bar b = new Bar();
        b ==> Bar@5419f379
    
        jshell> f.foo();
        Sub
    
        jshell> b.foo();
        Sub
    
        jshell> ((Foo)b).foo();
        Sub

~~~
frant-hartm
That's right, I forgot a method parameter:

    
    
      jshell> class Foo { void foo(Object o) { System.out.println("Super"); } }
      |  created class Foo
    
      jshell> class Bar extends Foo { void foo(Integer i) { System.out.println("Sub"); } }
      |  created class Bar
    
      jshell> Foo f = new Bar();
      f ==> Bar@7f9a81e8
    
      jshell> Integer i = 1;
      i ==> 1
    
      jshell> f.foo(i)
      Super
      
      jshell> ((Bar)f).foo(i);
      Sub

------
hrgiger
This blog post doesnt cover all interesting changes, at least for me.[0]

For me most interesting upcoming changes are

JEP: 352: Non_Volatile Mapped Byte Buffers, JEP 345: NUMA-Aware Memory
Allocation for G1 and JEP 370: Foreign-Memory Access API (Incubator).
Especially FMA api [1] examples seems most promising but shipped with panama
and I am not sure about the maturity yet: [https://github.com/zakgof/java-
native-benchmark](https://github.com/zakgof/java-native-benchmark)

I wonder how GraalVM stands in this picture that beside of being polyglot, it
has AOT and auto vectorization futures and I dont know if those are
already/will be shipped also with openJDK

[0] [https://jaxenter.com/java-14-update-
news-163585.html](https://jaxenter.com/java-14-update-news-163585.html) [1]
[https://openjdk.java.net/jeps/370](https://openjdk.java.net/jeps/370)

------
cutler
Wow - raw text but I still have to escape \s, \w and friends in a regex. Call
me underwhelmed.

------
time4tea
BankTransaction amount is a double? transactionDate is a LocalDate?

Please nobody copy paste that class.

~~~
sdfin
What's the problem with using LocalDate to store a date (without time)?

~~~
thu2111
There's no timezone.

LocalDate is appropriate for things like user interfaces, where you're
modelling an intuitive/vague concept of date-ness only meaningful in some
wider human context, or where the actual time at which that day starts just
doesn't matter or is unknown. For instance it may be a good type to use for
annotated historical events, where the day the event happened is the most
accurate you can get.

For a bank transaction where they're international by nature and usually need
to be ordered temporally against each other, it's an inappropriate type. The
time zone in which the date should be interpreted is important. But really for
transactions you'd be better off using Instant, at least internally. Time
matters too.

------
insertnickname
"Java 14 is scheduled for release on March 17"

So it didn't quite arrive yet.

~~~
filomeno
You know, candidate/preview releases have been available for some time, if you
can't wait to test these new features:

[https://jdk.java.net/14/](https://jdk.java.net/14/)

------
technoplato
So, (if I could write LaTek in here I would)

The limit as JavaVersion —> Inf = Kotlin?

I won’t complain.

------
dang
Related from a few weeks ago:
[https://news.ycombinator.com/item?id=22237145](https://news.ycombinator.com/item?id=22237145)

------
julius_set
Would be great if Records were actually bonafide value types lol as in not
allocated on the heap. Otherwise it’s just nice syntactic sugar like data
classes in Kotlin

~~~
eklavya
All in good time, value types is in progress. Available to try here:
[https://wiki.openjdk.java.net/display/valhalla/Minimal+Value...](https://wiki.openjdk.java.net/display/valhalla/Minimal+Value+Types)

~~~
julius_set
It’s been 6 years since Valhalla was announced why it’s still in prototype
befuddles me. If anything, getting value types out the door will have
significant performance improvements and least barrier of entry for current
java / jvm based programmers.

------
exabrial
How do I propose a language change? One thing I'd really like to see is
optional type declarations in Lambdas to bulletproof them. Take this simple
Lambda

    
    
       List<String> names = new ArrayList<>();
       names.stream().filter(String name -> "Bob Vance".equals(name)).findFirst().get();
    
    

By adding the String type declaration, a whole host of bugs in really
complicated Lambdas can be eliminated and found easier when the original types
and lists are being shuffled around

~~~
sickmartian
Can't you do .filter((String name) -> "Bob Vance".equals(name)) already?

~~~
exabrial
Oddly enough I'm having trouble finding references around this, even though it
most definitely compiles.

------
dbsmith83
Seems like Java is trying to catch up with Kotlin

------
justlexi93
Pattern Matching for instanceof. Non-Volatile Mapped Byte Buffers. Helpful
NullPointerExceptions. Switch Expressions (Standard) Packaging Tool
(Incubator) NUMA-Aware Memory Allocation for G1. JFR Event Streaming. Records
(Preview)

Everything looks promising for me.

------
dmitriid
Can't understand the need for records when C# solves the problem of
boilerplate with regular classes and some syntactic sugar.

~~~
Kipters
It's an easy and zero-boilerplate way to have immutable data-only structures
with some useful tidbits like structural comparison, meaningful hashcodes etc.

I say this as a C# developer that would _desperately_ want them supported in
C# and was super pissed off when they were discarded from C# 8.0 (I actually
need them right now, they would save me a whole day of typing today)

~~~
u7u7h
Records are coming
[https://github.com/dotnet/csharplang/projects/4#card-1849391...](https://github.com/dotnet/csharplang/projects/4#card-18493910).
Will be in C# 9

~~~
Kipters
hopefully yes, I'm following the Records v2 issue on github :D

------
anonymousiam
Who cares about the new features? The new license makes it almost impossible
to use Java without some form of payment to Oracle.

~~~
aw1621107
> The new license makes it almost impossible to use Java without some form of
> payment to Oracle.

Do you have a source for this claim? It sounds a bit extreme, to say the
least, and I had the impression OpenJDK was licensed using fairly standard
terms.

~~~
Spinfusor
It's mostly FUD: it's only relevant for the Oracle JDK; it doesn't apply to
OpenJDK (or the other open distributions by other orgs).

~~~
cxr
It doesn't help that multiple Oracle/Sun folks—including people like
McNealy—said under oath that they don't believe that the licensing permits you
to make commercial use, even if you opt for the GPL version.

~~~
pjmlp
At the time Google screwed Sun, the GPL version did not cover the deployment
into embedded platforms, only desktop and servers.

OpenJDK license is another matter.

~~~
cxr
I don't know what you're referring to, but FSF does not allow the GPL be used
in such a way that the four freedoms are compromised by the licensor imposing
additional restrictions.

~~~
pjmlp
Except that there are plenty of dual licenses with GPL-exception clauses and
Java was one of them back then.

It is up to the courts and copyright holder to decided what to do with their
IP.

~~~
cxr
First, you didn't describe an exception; you described additional
restrictions. But now you're pivoting to talk about exceptions.

These are fundamentally different things. One enlarges the set of actions a
recipient is free to do relative to what vanilla GPL allows. This is permitted
(and in the case of the classpath exception, endorsed) by FSF. The other
attempts to shrink the size of that set by denying the user things that the
GPL would otherwise allow. The FSF simply does not permit the GPL to be used
in that combination (and there would be extreme contrast in your last sentence
and the failure to recognize the FSF's say in this).

And secondly, you've yet to substantiate your claim that Java was ever
distributed with such GPL-modifying restrictions.

~~~
pjmlp
Well, I let Gosling speak about Google's then

[https://www.youtube.com/watch?v=ZYw3X4RZv6Y&feature=youtu.be...](https://www.youtube.com/watch?v=ZYw3X4RZv6Y&feature=youtu.be&t=57m42s)

~~~
cxr
How about a straightforward response, rather than trying to change the subject
again?

What's more, I've seen this interview multiple times. Listening to Gosling
stutter and be coy is not illuminating in the least. He has no idea how to
answer the question he was asked, much less what's being discussed here now.

Can you substantiate your claim or not?

~~~
pjmlp
Sun as copyright holder had the right to constraint Java's usage as they
wanted and embedded deployment wasn't covered.

Naturally it is hard for anyone to link to anything Sun, given what happened
with their assets and Internet presence.

Is a substantiate argument? Maybe not, it doesn't change the fact that Google
screwed Sun, didn't bothered to rescued it went it went down, and now we have
Java and Android Java.

I guess FSF is happy with the outcome then, since it is allowed to tank
companies.

~~~
cxr
> Sun as copyright holder had the right to constraint Java's usage

Sure. But what they don't have is domain over the GPL.

I won't respond to the rest of your comment, which has nothing to do with the
claim you made to kick off this branch of discussion and is just another
attempt to change the subject (with what is an opinion, not a "fact").

This will be my last comment here.

