Major OpenJDK projects

  • JDK=>JDK 14, JDK 15, JDK 16, ... --  features, APIs, performance, quality
  • Valhalla=>value types, generic specialization
  • Loom=>virtual threads (formerly "fibers")
  • Panama=>native memory & functions, vectorization
  • Amber=>productivity-oriented languages features

Amber features

 

  • Local variable type inference ("var") --Java 10--
  • Text blocks --preview in Java 14--
  • Switch expressions --Java 14--
  • Records --preview in Java 14--
  • Sealed types --future--
  • Pattern matching for instanceof --preview in Java 14--

Local variable type inference ("var")

*For declaring local variables only, can use car instead of type

  • =>this is still static typing -- the compiler infers the type
  • =>Java is not turning into JavaScript!

*Isn't one of Java's strengths that types are declared explicitly?

  • =>static typing is still a strength
  • =>sometimes a variable's type is unimportant in context
  • =>in such cases, explicit types sometimes add clutter

Example of local variable type inference ("var"): cluttered code

Example of local variable type inference ("var"): clean code

Text blocks


*A "two-dimensional" string literal

 

  • =>supports multiple lines without escape sequences and appending
  • =>newlines mormalized to \n for cross-platform repeatability
  • =>supports standard escape sequences, plus additional for newline and whitespace control

*"Smart" policy for removal of incidental whitespace

  • =>allows reindentation of code without affecting string contents

*New String APIs

  • =>formatted(), stripIndent(), translateEscapes()

Example of text block with syntactic noise

Example of text block without syntactic noise

*Concerning the new line conventions between Linux and Windows,

if you take this code from Linux to Windows.
The new line separator is simply the "NL" Character not the blind feed character as opposed to carriage return linefeed.

*Concerning the leading whitespace,

the string before the leftmost line is called the incidental white space, and it is stripped away.

Switch expressions

*Historically, switch is a statement

 

  • =>requires break in each case --easy to forget
  • =>sometimes want to do an assignment in each case --easy to forget
  • =>possible to omit a case, in which (silently) nothing happens (#might not be what you want)  

*In these cases, intent can be expressed better with an expression

 

  • =>typically uses new label form -> to avoid the need for a break statement
  • =>note: -> is not a lambda expression
  • =>expression must be exhaustive
  • =>all values must be handled, or default case provided

Example of switch statement

Example of switch expression

*Exhaustive -- no default clause needed, as all cases covered
*Exhaustiveness allows compiler to distinguish between "I want to handle all cases explicitly" from "I want to handle some cases but not others".

Records

*A transparent, shallowly immutable data aggregate class

  • =>an aggregation of named fields
  • =>every fields has a correspondly-named public accessor method
  • =>sort of like a nominal tuple

*A record is a class

  • =>equals(), hashCode(), toString() are provided and can be overridden
  • =>constructor can be overridden to perform argument validation
  • =>additional methods are allowed
  • =>can implement interfaces
  • =>API is entirely defined by state description (i.e, fields)

*Records are restricted class

*Cannot be subclassed

  • =>tremendously complicates semantics of equals()
  • =>avoids issues with accidental "decapitation" --when upcasting--
  • =>fields ("record component") form the complete state of a record

*Are shallowly immutable

  • =>all fields are implicitly final
  • =>can contain references to mutable objects
  • =>firmer foundation for later features, e.g. deconstruction (see later)

Example before use of Record

Example with use of Record

*The constructor, equals(), hashCode(), toString(), and component accessors --x() & y()-- are all provided automatically.

record use case

*A local record, like a local class. A class has too much syntactic overhead to use this way.

Sealed types

*Allows a class to control its subclasses

  • =>"final" is too restrictive -- prevents all subclassing even with library
  • =>workarouund is to provide only package-private constructor
  • =>inflexible: only works with classes, not interfaces
  • =>example: EnumSet subclasses RegularEnumSet and JumboEnumSet

*Sealed types

 

  • =>allows class or interface author to control subclasses/implementations
  • =>compiler and VM enforce well-defined set of subclasses/impls
  • =>explicitly using "permits" clause
  • =>or implicit to classes in the same file (including nested classes)

Example of sealed types

Example of a variant of sealed types

*"permits" clause inferred

Pattern matching for instanceof - before it

*type name repeated, obscur syntax. Most of the time there is a cast after instanceof

Pattern matching for instanceof

*Provide a binding variable name with instanceof

  • =>implicitly declares a new local variable
  • =>its type is the type name from instanceof
  • =>initialized by downcasting the reference to the instanceof type

*Use cases

  • =>probably gets rid of 90% of casts in existing code
  • =>quite useful in implementing equals()

Pattern matching for instanceof - now

*there is a new local variable initialized by cast, whom scope is subsequent block

Pattern matching for instanceof - a more complicated example: old equals

Pattern matching for instanceof - a more complicated example: new equals

Putting it all together

*Just a bunch of individual features, right?

  • =>switch expressions
  • =>records
  • =>sealed types
  • =>pattern matching for instanceof

*Features designed to work together
*Future enhancements in-progress:
=>add type patterns to switch expressions

  • =>with a sealed type, such a switch can enforce exhaustiveness
  • =>records allow deconstruction patterns

Putting it all together - Remember the previous sealed types

Putting it all together - Application to switch: type pattern

*no default case, since Shape is a sealed type with exactly two subtypes

Putting it all together - Application to switch: deconstruction pattern

Putting it all together - Application to instanceof: deconstruction pattern

*1 is the value of the radius

Links of project Amber

  • Project page - has links to all the JEPs

=> http://openjdk.java.net/projects/amber/

  • See also Bierman & Goetz, Pattern Matching for Java

=> http://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html