@benjiweber
Streams
CompletableFutures
Optionals
Language features can become library features
Without magic
Readable
Debuggable
() -> {
someCode();
}
void foo() {
someCode();
}
(param1, param2) -> {
return "foo";
}
String foo(String param1, String param2) {
return "foo";
}
BiFunction<String, String, String> f = (param1, param2) -> {
return "foo";
}
Extend
public static <T> Optional<T>
when(boolean condition, Supplier<T> then) {
return condition
? Optional.ofNullable(then.get())
: Optional.empty();
}
try (BufferedReader br = reader()){
br.read();
}
using(reader(), br -> {
br.read();
});
public static <T extends AutoCloseable>
void using(T t, ExceptionalConsumer<T> consumer) {
try {
consumer.accept(t);
} finally {
try {
t.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Java Laws of physicS
Lambdas
Method References
Default Interface Methods
Static Interface Methods
Structural Typing
Function<String, Boolean> f = s -> true;
Predicate<String> p = s -> true;
new File(".").list((name, file) -> true);
BiFunction<Integer, Integer, Integer> adder = (a,b) -> a+b;
interface Calculation {
int operation(int a, int b);
}
Calculation adder = (a,b) -> a+b;
java.util.function.*
(like primitives)
track("example.com", userData, CLICK);
track("example.com", userData, PLAY);
interface ExampleComTracker {
void track(EventType event);
}
ExampleComTracker exampleCom = event ->
track("example.com", userData, event);
exampleCom.track(CLICK);
exampleCom.track(PLAY);
public void doSomething(List<String> strings) { }
public void doSomething(List<Integer> ints) { }
public void doSomething(List<> strings) { }
public void doSomething(List<> ints) { }
public interface ListStringRef extends Supplier<List<String>>{}
public void doSomething(ListStringRef strings) { }
public interface ListIntegerRef extends Supplier<List<Integer>>{}
public void doSomething(ListIntegerRef ints) { }
doSomething(() -> asList("aa","b"));
doSomething(() -> asList(1,2));
void update(int volume) { }
Max value?
Where are all the places volume is used?
Why is it needed?
void update(Volume newValue) { }
public class Volume {
private int volume;
public Volume(int volume) {
if (volume > 11) throw new IllegalArgumentException();
this.volume = volume;
}
public int getVolume() {
return volume;
}
public void setVolume(int volume) {
this.volume = volume;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Volume volume1 = (Volume) o;
return volume == volume1.volume;
}
@Override
public int hashCode() {
return volume;
}
@Override
public String toString() {
return "Volume{" +
"volume=" + volume +
'}';
}
}
All I wanted was a single value
public class Volume {
public final int value;
public Volume(int value) {
if (value > 11) throw new IllegalArgumentException();
this.value = value;
}
}
public interface Volume {
int value();
static Volume volume(int value) { return () -> value; }
}
update(volume(11));
public interface Volume {
int value();
static Volume volume(int value) {
if (value > 11) throw new IllegalArgumentException();
return () -> value;
}
}
update(volume(11));
public interface Volume {
int value();
static Volume volume(int value) { return () -> value; }
default Volume turnUp() { return volume(value() + 1); }
}
interface Paint {
int red();
int green();
int blue();
static Paint paint(int red, int green, int blue) {
return new Paint() {
public int red() { return red; }
public int green() { return green; }
public int blue() { return blue; }
};
}
}
interface Paint {
int red();
int green();
int blue();
static Paint create(int red, int green, int blue) {
abstract class PaintValue extends Value<Paint>
implements Paint {}
return new PaintValue() {
public int red() { return red; }
public int green() { return green; }
public int blue() { return blue; }
}.using(Paint::red, Paint::green, Paint::blue);
}
}
public class Value<T> {
public T using(Function<T,?>... props) {
this.props = asList(props);
}
int hashCode() {
return props.stream()...
}
}
Builder Pattern
Person person = new Person("benji", "weber", 180);
Person person = new Person(
firstName: "benji",
lastName: "weber",
heightInCentimetres: 180
);
Person person = person()
.firstName("benji")
.lastName("weber")
.height(centimetres(180));
public class PersonBuilder {
private String firstName;
private String lastName;
private Person.Centimetres height;
public PersonBuilder setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public PersonBuilder setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public PersonBuilder setHeight(Person.Centimetres height) {
this.height = height;
return this;
}
public Person createPerson() {
return new Person(firstName, lastName, height);
}
}
All I wanted was names
Structural Typing
Person person = person()
.firstName("benji")
.lastName("weber")
.height(centimetres(180));
public static SpecifyFirstName person() {
return firstName
-> lastName
-> height
-> new Person(firstName, lastName, height);
}
public interface SpecifyFirstName {
SpecifyLastName firstName(String firstName);
}
public interface SpecifyLastName {
SpecifyHeight lastName(String lastName);
}
public interface SpecifyHeight {
Person height(Centimetres height);
}
Half as long
Compile-time safety
shape.match()
.when(Rectangle.class,
rect -> "Rectangle " + rect.width() + "x" + rect.length())
.when(Circle.class,
circle -> "Circle with radius " + circle.radius())
.when(Cube.class,
cube -> "Cube with size " + cube.size());
interface Shape extends Case3<Rectangle, Circle, Cube> { }
interface Cube extends Shape { ... }
public interface Case3<T,U,V> {
default MatchBuilderNone<T,U,V> match() { ...
String address = customer.match()
.when(a(Customer::customer).matching(
"Benji",
"Weber",
an(Address::address).matching(
a(FirstLine::firstLine).matching(_,_),
_
)
)).then((houseNo, road, postCode) -> houseNo + " " + road + " " + postCode)
.otherwise("unknown");.
asList("foo","bar")
.stream()
.map(String::length)
.collect(Collectors.toList());
asList("foo","bar")
.map(String::length);
interface ForwardingList<T> extends List<T> {
List<T> impl();
default int size() {
return impl().size();
}
...
}
interface Mappable<T> extends ForwardingList<T> {
default <R> List<R> map(Function<T,R> f) {
return impl().stream().map(f).collect(Collectors.toList());
}
}
Mappable<T> mappable = () -> asList("foo","bar");
List<Integer> lengths = mappable.map(String::length);
How do I express myself, in Java
@benjiweber
Send me your missing lang features!
talent@unrulygroup.com