Project Amber

The SolutionFactory
To Java's Problems

Developer Advocate

Java Team at Oracle

Java’s Got Problems

  • slow

  • old

  • verbose

  • cumbersome

  • no fun!

None of that is true.

But none of it is (entirely) false, either.

Java’s Got Problems

A more nuanced view
(on some issues):

  • strings lack expressiveness

  • the type system and encapsulation
    can get in the way

  • dealing with data is clunky

  • getting started isn’t easy

The SolutionFactory

Project Amber:

Right-sizing ceremony.

(Things that are important but cumbersome
should be made less cumbersome.)

Profile:

Project Amber

Goal is not:

  • code golf

  • feature overload

Project Amber

Instead, identify situations where:

  • Java lacks expressiveness

  • strengths turn into weaknesses

  • tensions and tradeoffs occur

And resolve those for greater good!

Nota Bene

Amber is not the only project doing that!

  • Valhalla — design vs performance

  • Loom — simplicity vs scalability

  • Panama — flexibility vs safety

  • Leyden — dynamism vs performance

Table of Contents

Getting back to Project Amber:

  • strings

  • type safety & encapsulation

  • dealing with data

  • starting (with) Java

Strings

How Project Amber
smartly enhances
one of Java’s most basic types.

String Issues

String json = "{\n"
	+ "\t\"name\": \"" + name + "\",\n"
	+ "\t\"year\": \"" + bday.getYear() + "\"\n"
	+ "}";

What we’re expressing:

  • we need a String βœ… πŸ˜ƒ

  • across multiple lines 🫠

  • with indentation 🫣

  • and quotation marks 😒

  • and embedded variables 😭

String Issues

String literals lack expressiveness:

  • no concept of multi-line-ness

  • no concept of interpolation/processing

  • no concept of rawness

Project Amber is working on that.
(But without throwing it all into one pot.)

Text Blocks

Java 15 finalized text blocks (JEP 378):

  • multiline string literals

  • understand incidental vs essential indentation

  • require less escaping

String duke = """
	{
		"name": "Duke",
		"year": "1992"
	}
	""";

We can now "enable" multi-line-ness.

Text Blocks

But embedding variables is still cumbersome.

With String::formatted:

String json = """
	{
		"name": "%s",
		"year": "%d"
	}
	""".formatted(name, bday.getYear());

String Templates

Java 21/22 preview string templates (JEP 430 / 459):

  • allow embedding expressions

  • understand need for explicit processing
    (for validation, escaping, etc.)

String json = STR."""
	{
		"name": "\{name}",
		"year": "\{bday.getYear()}"
	}
	""";

We can now "enable" variable embedding.

String Templates

Template expression:

String json = STR."""
	{
		"name": "\{name}",
		"year": "\{bday.getYear()}"
	}
	""";

Ingredients:

  • template with embedded expressions: StringTemplate

  • template processor (e.g. STR): StringTemplate ⇝ String*

Why strings?

Often, strings are just exchange format, e.g.:

  • start with: String + values

  • validate / sanitize (i.e. parse)

  • dumb down to: String πŸ€”

  • parse to: JSONObject, Statement, …

Why the detour?

Custom templating

STR is a singleton instance of
a Processor implementation:

public interface Processor<RESULT, EX> {
	RESULT process(StringTemplate s) throws EX;
}

RESULT can be of any type!

Custom templating

// validates & escapes JSON
JSONObject doc = JSON."""
	{
		"name": "\{name}",
		"year": "\{bday.getYear()}"
	}
	""";

// prevents SQL injections
Statement query = SQL."""
	SELECT * FROM Person p
	WHERE p.name = '\{name}'
	""";

Raw Strings

  • proposed for Java 12 (JEP 326)

  • withdrawn due to complexity

  • would be convenient in some form

String regex = "\\[\\d{2,4}\\]"; // πŸ€”πŸ€”πŸ€”
// made-up syntax!
String rawRegex = !"\[\d{2,4}\]" // πŸ€”

Maybe, in the future, we can "enable" rawness. 🀞
(But no plans at the moment.)

Summary

Java’s strings are:

  • essential to development

  • not expressive

Project Amber introduces new features that:

  • make strings more expressive

  • learned from other languages

  • can be combined as needed

Type Safety & Encapsulation

How Project Amber tackles the tension
explicitness/safety vs succinctness
for less verbosity and new features.

Type Safety

Java is…​

strongly typed:

every field, variable, method argument
and return has a type

statically typed:

all this is known at compile time

Encapsulation

For our types, fields, methods:

  • decouple API from internal state

  • hide internals (behind accessors)

  • pick lowest feasible visibility

Type Safety & Encapsulation

We rely on them to:

  • model domains

  • guarantee invariants

  • modularize problems

  • catch errors early

  • understand code

Type Safety & Encapsulation

But, they can be:

  • verbose

  • redundant

Underlying tension:
explicitness/safety vs succinctness.

Throwback: Lambdas

Predicate<Person> isOld = person -> person.age() >= 30;
  • didn’t allow anything new!

  • focus on what’s essential
    (behavior and semantics)

  • removed lots of verbosity
    (and bits of explicitness)

Found a (situationally) better balance.
⇝ More "code as data".

Project Amber

Aims to do more of that:

  • give up small amounts of benefits

  • in suitable, specific situations

  • for new semantics and features

  • for much more succinctness

Local-Variable Type Inference

Java 10 introduced var (JEP 286):

var audienceMember = new Person("John Doe");
  • focus on what’s essential
    (variable name and expression)

  • removes verbosity & redundancy
    (and bits of explicitness)

Finds a better balance (if used wisely).
⇝ More readable variables.

Records

Java 16 introduced records (JEP 395):

record Person(String name, LocalDate birthday) { }

Transparent carriers for immutable data.

  • compiler understands internals

  • couple API to internals

  • reduce verbosity a lot

Defines a new point of balance.
⇝ More "data as data".

Pattern Matching

In if since Java 16 (JEP 394),
in switch since Java 21 (JEP 441):

if (obj instanceof Person person) {
	var name = person.name();
	var bday = person.birthday();
	// use `name` and `bday`
}

switch (obj) {
	case Person person -> {
		// as above...
	}
	// ...
}

Destructuring Records

Java 21 introduced record patterns (JEP 440):

if (obj instanceof Person(var name, var bday)) {
	// use `name` and `bday`
}

switch (obj) {
	case Person(var name, var bday) -> {
		// use `name` and `bday`
	}
	// ...
}

Destructuring Records

var person = fetchPerson();
var name = person.name();
var bday = person.birthday();
// use `name` and `bday`

In the future (no JEP, but it’s coming):

// speculative syntax
Person(var name, var bday) = fetchPerson();
// use `name` and `bday`

Destructuring Records

var person = fetchPerson();
var unnamed = new Person("", person.birthday());

Maybe functional mutation in the future
(design document from Aug 2020):

// highly speculative syntax
var person = fetchPerson();
var unnamed = person with {
	name = "";
};

Summary

Type safety and encapsulation:

  • are bedrocks of Java

  • but aren’t free

Project Amber introduces new features that:

  • lower the cost

  • make them shine brighter

Dealing With Data

How Project Amber introduces
a new programming paradigm
to handle data as data.

Data-Oriented Problems

Essentials when dealing with data
(JSON, SQL result, events, …):

  • represent data with simple, immutable types

  • model alternatives explicitly

  • make illegal states unrepresentable

  • represent polymorphic behavior with functions

  • (de)construct data easily and quickly

Example: GitHub Crawler

Starting with a seed URL:

  1. identifies kind of page

  2. follows links from GitHub pages (⇝ 1.)

public static Page loadPageTree(/*...*/) {
	// ...
}

What does Page look like?

Data Representation

"Represent data with simple immutable types." βœ…

Records:

public record ErrorPage(
	URI url, Exception ex) { }
public record ExternalPage(
	URI url, String content) { }
public record GitHubIssuePage(
	URI url, String content,
	Set<Page> links, int issueNumber) { }
public record GitHubPrPage(
	URI url, String content,
	Set<Page> links, int prNumber) { }

Modeling Alternatives

"Model alternatives explicitly." ❓

Use sealed types to limit inheritance.

Sealed Types

Java 17 introduced sealed types (JEP 409):

public sealed interface Page
		permits ErrorPage, SuccessfulPage {
	// ...
}

Only ErrorPage and SuccessfulPage
can implement/extend Page.

⇝ interface MyPage extends Page doesn’t compile

Modeling Alternatives

public sealed interface Page
        permits ErrorPage, SuccessfulPage {
    URI url();
}

public sealed interface SuccessfulPage
        extends Page permits ExternalPage, GitHubPage {
    String content();
}

public sealed interface GitHubPage
        extends SuccessfulPage
        permits GitHubIssuePage, GitHubPrPage {
    Set<Page> links();
    default Stream<Page> subtree() { ... }
}

Illegal States

"Make illegal states unrepresentable." βœ…

Combine:

  • sealed types

  • records

  • data validation
    (during construction)

(Also makes code more self-explanatory.)

Representing Behavior

"Represent polymorphic behavior with functions." βœ…

(Static) methods that have data as input:

public static String createPageName(Page page) {
	// ...
}

"Polymorphic"❓

Data-Driven Polymorphism

Switch over input type:

public static String createPageName(Page page) {
	return switch (page) {
		case ErrorPage err
			-> "πŸ’₯ ERROR: " + err.url().getHost();
		case ExternalPage ext
			-> "πŸ’€ EXTERNAL: " + ext.url().getHost();
		case GitHubIssuePage issue
			-> "🐈 ISSUE #" + issue.issueNumber();
		case GitHubPrPage pr
			-> "πŸ™ PR #" + pr.prNumber();
		// ...
	};
}

Data-Driven Polymorphism

To keep code maintainable:

  • switch over sealed types

  • enumerate all possible types

  • avoid default branch

switch (page) {
	case ErrorPage err -> // ...
	case ExternalPage ext -> // ...
	case GitHubIssuePage issue -> // ...
	case GitHubPrPage pr -> // ...
	// no default branch!
}

⇝ Compile error when new type is added.

Deconstructing Data

"Deconstruct data easily and quickly" βœ…

Use deconstruction patterns:

public static String createPageName(Page page) {
	return switch (page) {
		case ErrorPage(var url, var ex)
			-> "πŸ’₯ ERROR: " + url.getHost();
		case GitHubIssuePage(
				var url, var content, var links,
				int issueNumber)
			-> "🐈 ISSUE #" + issueNumber;
		// ...
	};
}

Ignoring Data

Java 22 finalizes unnamed patterns (JEP 456).
Use _ to ignore components:

public static String createPageName(Page page) {
	return switch (page) {
		case ErrorPage(var url, _)
			-> "πŸ’₯ ERROR: " + url.getHost();
		case GitHubIssuePage(_, _, _, int issueNumber)
			-> "🐈 ISSUE #" + issueNumber;
		// ...
	};
}

⇝ Focus on what’s essential.

Avoiding Default

Use _ to define default behavior:

public static String createPageEmoji(Page page) {
	return switch (page) {
		case GitHubIssuePage issue -> "🐈";
		case GitHubPrPage pr -> "πŸ™";
		case ErrorPage _, ExternalPage _ -> "n.a.";
	};
}

⇝ Default behavior without default branch.

Data-Oriented Programming

Use Java’s strong typing to model data as data:

  • use types to model data, particularly:

    • data as data with records

    • alternatives with sealed types

  • use (static) methods to model behavior, particularly:

    • exhaustive switch without default

    • pattern matching to destructure polymorphic data

Data-Oriented Programming…

… isn’t funtional programming
  • but it’s similar (data + functions)

  • first priority is data, not functions

… doesn’t kill object-oriented programming
  • use OOP to modularize large systems

  • use DOP to model small, data-focused (sub)systems

More

More on data-oriented programming:

Summary

Object-oriented programming:

  • is the beating heart of Java develoment πŸ’“

  • but isn’t the best fit in all situations

Project Amber introduces new features that:

  • unlock data-oriented programming

  • make functional programming more feasible

Starting (With) Java

How Project Amber
paves the on-ramp
for new (Java) developers.

Why Do We Care?

We all know Java, IDEs, build tools, etc.

  • do we all?

  • what about your kids?

  • what about students?

  • what about the frontend dev?

  • what about ML/AI folks?

Java needs to be approachable!
Java needs an on-ramp for new (Java) developers!

Running Java

To write and run a simple Java program, you need:

  • a JDK

  • an editor (IDE?)

  • javac (build tool? IDE?)

  • java (IDE?)

  • some Java code

Writing Java

Minimal Java code:

public class Main {
	public static void main(String[] args) {
		System.out.println("Hello, World!");
	}
}
  • visibility

  • classes & methods

  • static vs instance

  • returns & parameters

  • statements & arguments

Approachability

That’s a lot of tools and concepts!

Java is great for large-scale development:

  • detailed toolchain

  • refined programming model

This make it less approachable.

Let’s change that!

jshell

Java 9 added jshell:

  • all you need:

    • tools: JDK, jshell

    • concepts: statements & arguments

  • but:

    • not great for beginners (IMO)

    • no progression

More is needed.

Single-file Execution

Java 11 added single-file execution (JEP 330):

java Prog.java
  • removed: javac

  • but: no progression

Much better for beginners,
but just a section of an on-ramp.

On-Ramp

Expand single-file execution in two directions:

  • simplify code: reduce required Java concepts

  • ease progression: run multiple files with java

Simpler Code

Remove requirement of:

  • String[] args parameter

  • main being static

  • main being public

  • the class itself

// all the code in Prog.java
void main() {
	System.out.println("Hello, World!");
}

Previews in Java 21/22 (JEP 445 / 463).

Running Multiple Files

Say you have a folder:

MyFirstJava
 β”œβ”€ Prog.java
 β”œβ”€ Helper.java
 └─ Lib
     └─ library.jar

Run with:

java -cp 'Lib/*' Prog.java

Added in Java 22 (JEP 458).

Progression

Natural progression:

  • start with main()

  • need arguments? ⇝ add String[] args

  • need to organize code? ⇝ add methods

  • need shared state? ⇝ add fields

  • need more functionality? ⇝ explore JDK APIs

  • even more? ⇝ explore simple libraries

  • need more structure? ⇝ split into multiple files

  • even more ⇝ use visibility & packages

Doesn’t even have to be that order!

Summary

Java’s strengths for large-scale development
make it less approachable:

  • detailed toolchain

  • refined programming model

Project Amber introduces new features that:

  • make it easier to start

  • allow gradual progression

  • entice the future dev generation

Project Amber

Up to Java 20:

  • var

  • text blocks

  • records

  • type patterns

  • sealed types

Project Amber

In Java 21:

  • pattern matching in switch

  • record patterns

  • unnamed patterns (preview)

  • string templates (preview)

  • simpler main (preview)

Project Amber

In Java 22:

  • unnamed patterns

  • multi-file launcher

  • string templates (preview)

  • simpler main (preview)

Project Amber

And beyond:

  • deconstruction assignments

  • with expression

Project Amber

AND MORE:

Project Amber

The solution factory

to Java’s problems!

So long…​

37% off with
code fccparlog

bit.ly/the-jms

More

Slides at slides.nipafx.dev
β‡œ Get my book!

Follow Nicolai

nipafx.dev
/nipafx

Follow Java

inside.java // dev.java
/java    //    /openjdk

Image Credits