Fun with var

Lots to tinker with!

Rules of var
Intersection Types
Traits
Ad-hoc Fields And Methods

Fun with var

Rules of var
Intersection Types
Traits
Ad-hoc Fields And Methods

Code

What About Readability?

This is about readability!

  • less redundancy

  • more intermediate variables

  • more focus on variable names

  • aligned variable names

Code

Style Guidelines

Principles from the official style guidelines:

  1. Reading code is more important than writing it.

  2. Code should be clear from local reasoning.

  3. Code readability shouldn’t depend on IDEs.

  4. Explicit types are a tradeoff.

Style Guidelines

Guidelines:

  1. Choose variable names that provide useful info.

  2. Minimize the scope of local variables.

  3. Consider var when the initializer provides sufficient information to the reader.

  4. Use var to break up chained or nested expressions.

  5. Don’t worry too much about "programming to the interface".

  6. Take care when using var with diamonds or generics.

  7. Take care when using var with literals.

Code

More on var basics

Fun with var

Rules of var
Intersection Types
Traits
Ad-hoc Fields And Methods

Motivation

Sometimes you need a type
that implements two interfaces
without creating a new interface.

For example:

You need something
that is Closeable and an Iterator
without creating CloseableIterator.

Intersection types

Given two types, the intersection type
is the set of variables that are of both types.

An intersection type has the API of both types!

For example:

A variable of type Closeable & Iterator<E>
is both Closeable and an Iterator<E>.

It has close() and hashNext()/next()

Code

Intersection types

  1. in method signatures, express with generics:

    public <T extends Closeable & Iterator<String>>
    	T createCloseableIterator(...) { ... }
    
    public <E, T extends Closeable & Iterator<E>>
    	E firstMatch(T elements, ...) { ... }
  2. for variables use var:

    var elements = createCloseableIterator(true);
    firstMatch(elements, ...);

Evaluation

Downsides:

  • combination of non-trivial Java features:

    • generics with bounded wildcards

    • type inference

  • refactoring becomes harder

But:

  • intersection types are known concept

  • can be really helpful in a bind

Add to tool box; use with care.

More on intersection types

I’ve written a blog post:
nipafx.dev/java-var-intersection-types

Fun with var

Rules of var
Intersection Types
Traits
Ad-hoc Fields And Methods

Motivation

Sometimes you need to attach
prepared functionality to an instance
without creating a new type.

For example:

You have a Megacorp instance and an
IsSuccessful-interface that you
want to attach to it.

Traits

A trait extends an interface
and implements additional behavior.

The language needs to offer a simple way
to "attach" that trait to an instance at hand.

Code

Traits

  1. create a functional, delegating interface:

    @FunctionalInterface
    interface MegacorpDelegate extends Megacorp {
    	Megacorp delegate();
    	// implement `Megacorp` with default methods
    	// by forwarding calls to `delegate()`
    }
  2. create traits as interfaces:

    interface IsEvil extends Megacorp {
    	default boolean isEvil() { return true; }
    }

Traits

  1. cast lambda to desired intersection
    and assign to var-ed variable:

    var corp = (MegacorpDelegate & IsEvil) () -> original;

Evaluation

Downsides:

  • combination of non-trivial Java features:

    • lambda as poly expression

    • type inference

    • default methods

  • refactoring becomes harder
    (see intersection types)

  • delegating interface is cumbersome

  • breaks in collections (!)

Never use in "real" code!

More on traits

I’ve written a blog post:
nipafx.dev/java-var-traits

Fun with var

Rules of var
Intersection Types
Traits
Ad-hoc Fields And Methods

Motivation

Sometimes you need to extend a type
with a field or a method.

But not enough to create a new subtype.

Maybe with an anonymous class?

Anonymous class

new SimpleMegacorp(...) {
	final BigDecimal SUCCESS_BOUNDARY =
		new BigDecimal("1000000000000");

	boolean isSuccessful() {
		return earnings()
			.compareTo(SUCCESS_BOUNDARY) > 0;
	}
};

Code

Ad-hoc fields & methods

  1. create anonymous class with
    additional fields and/or methods

  2. assigned to var-ed variable

var corp = new SimpleMegacorp(...) {
	final BigDecimal SUCCESS_BOUNDARY =
		new BigDecimal("1000000000000");

	boolean isSuccessful() {
		return earnings()
			.compareTo(SUCCESS_BOUNDARY) > 0;
	}
};
corp.isSuccessful();

Evaluation

Downsides:

  • anonymous class is verbose (e.g. in stream)

  • combination of non-trivial Java features:

    • anonymous classes

    • type inference

  • impedes refactoring (!)

Prefer the alternatives!

Alternatives

Alternatives for ad-hoc fields:

Alternatives for ad-hoc methods:

  • extending base types

  • utility methods

  • traits

Records

What are records?
Upcoming Java language feature!

public record Range(int low, int high) {
	// compiler generates:
	//  * constructor, deconstructor
	//  * equals/hashCode/toString
	//  * accessors low(), high()
}
  • no boilerplate for plain "data carriers"

  • no room for error in equals/hashCode

  • makes Java more expressive

Customized records

public record Range(int low, int high) {

	// compiler knows signature and assigns to fields
	public Range {
		if (low > high)
			throw new IllegalArgumentException();
	}

	public void setLow(int low) {
		if (low > this.high)
			throw new IllegalArgumentException();
		this.low = low;
	}

}

Record trade-offs

The API for a record models the state, the whole state, and nothing but the state.

The deal:

  • give up encapsulation

  • couple API to internal state

  • get API for free

More on records

Developed under Project Amber:

More on records:

No known ETA; my guess:
not in 2019, likely not in 2020.

More on ad-hoc fields and methods

Summary

  • use var to improve readability:

    • right-hand side should be informative

    • variable name should be well-chosen

    • add more intermediate variables

  • tricks with var are usually complex:

    • may be worth it for intersection types

    • break equals/hashCode for traits

    • not worth it for ad-hoc fields and methods

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/jms

Follow

nipafx on
🐦 // // 📺 // 🐙🐱

More

⇜ Get my book!

Hire me as a trainer
(Java 8+, JUnit 5)

Image Credits