public <T extends Closeable & Iterator<String>>
T createCloseableIterator(...) { ... }
public <E, T extends Closeable & Iterator<E>>
E firstMatch(T elements, ...) { ... }
var
Rules of var |
Intersection Types |
Traits |
Ad-hoc Fields And Methods |
Slides at slides.nipafx.dev.
Code at github.com/nipafx/demo-java-x.
var
Rules of var |
Intersection Types |
Traits |
Ad-hoc Fields And Methods |
This is about readability!
less redundancy
more intermediate variables
more focus on variable names
aligned variable names
Principles from the official style guidelines:
Reading code is more important than writing it.
Code should be clear from local reasoning.
Code readability shouldn’t depend on IDEs.
Explicit types are a tradeoff.
Guidelines:
Choose variable names that provide useful info.
Minimize the scope of local variables.
Consider var
when the initializer provides sufficient information to the reader.
Use var
to break up chained or nested expressions.
Don’t worry too much about "programming to the interface".
Take care when using var
with diamonds or generics.
Take care when using var
with literals.
var
basicsFirst Contact With var
In Java 10
💻 tiny.cc/java-var /
â–¶ tiny.cc/java-var-yt
cheat sheet (⇜ print when getting started!)
var
Rules of var |
Intersection Types |
Traits |
Ad-hoc Fields And Methods |
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
.
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()
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, ...) { ... }
for variables use var
:
var elements = createCloseableIterator(true);
firstMatch(elements, ...);
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.
I’ve written a blog post:
nipafx.dev/java-var-intersection-types
var
Rules of var |
Intersection Types |
Traits |
Ad-hoc Fields And Methods |
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.
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.
create a functional, delegating interface:
@FunctionalInterface
interface MegacorpDelegate extends Megacorp {
Megacorp delegate();
// implement `Megacorp` with default methods
// by forwarding calls to `delegate()`
}
create traits as interfaces:
interface IsEvil extends Megacorp {
default boolean isEvil() { return true; }
}
cast lambda to desired intersection
and assign to var
-ed variable:
var corp = (MegacorpDelegate & IsEvil) () -> original;
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!
I’ve written a blog post:
nipafx.dev/java-var-traits
var
Rules of var |
Intersection Types |
Traits |
Ad-hoc Fields And Methods |
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?
new SimpleMegacorp(...) {
final BigDecimal SUCCESS_BOUNDARY =
new BigDecimal("1000000000000");
boolean isSuccessful() {
return earnings()
.compareTo(SUCCESS_BOUNDARY) > 0;
}
};
create anonymous class with
additional fields and/or methods
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();
Downsides:
anonymous class is verbose (e.g. in stream)
combination of non-trivial Java features:
anonymous classes
type inference
impedes refactoring (!)
Prefer the alternatives!
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
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;
}
}
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
Developed under Project Amber:
led by Brian Goetz
launched March 2017
project / wiki / mailing list
More on records:
No known ETA; my guess:
not in 2019, likely not in 2020.
I’ve written a blog post:
nipafx.dev/java-var-anonymous-classes-tricks
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