Avec la version 9 sortie en septembre 2017, Java appuie sur la pédale ! Le rythme des livraisons passe à une version majeure tous les 6 mois. Java 10 est sorti en mars, prochaine version en septembre. Java 10 apporte le 'var' et l'inférence de type pour les variables locales. D'autres nouveautés sont en préparation : les constantes dynamiques, les classes de données, un nouveau switch à base de lambda, des interfaces fermées, de nouvelles choses du coté des génériques et bien plus encore.
Cela viendra-t-il en 11, 12, 15 ? Ne spéculons pas, mais quand ces nouveautés seront prêtes, elles sortiront en quelques mois. On se propose de présenter ces nouveautés, celles qui sont presque prêtes, celles qui seront prêtes bientôt, et celles qui ne seront pas prêtes avant un moment. Quels seront les impacts sur le langage, sur la JVM et donc sur les performances ? Que cela va-t-il nous apporter au quotidien, en tant que développeurs ? Quels seront les nouveaux patterns ? Voici le programme de cette présentation, avec des slides, du code, de la joie et de la bonne humeur !
5. 5
José in a few more wordsJosé in a few more words
@JosePaumard
https://github.com/JosePaumard/
https://josepaumard.github.io/
https://www.youtube.com/user/JosePaumard
7. 7
Public Poll!
You can get Kahoot! to participate in the poll, just before
the coffee break
8. 8
Introduction: What is Java up to?
What is going on behind the curtain?
How is Java organized: the release train etc…
What is underway for the language and the VM?
10. 10
OpenJDK Enhancement Proposal
JEP 1: Roadmap Process
JEP 11: Incubator Modules (Java 9)
JEP 12: Preview Language and VM feature (Java 11)
JEP 248: Make G1 the default GC (Java 9)
JEP 286: Local-Variable Type Inference (Java 10)
JEP 291: Deprecate CMS (Java 9)
JEP 326: Raw String Literals (Java 11)
...
11. 12
jdk
Amber
var condy expr switch record
Valhalla
nest mate
value type
preview
jdk 9
release
jdk 10
release
jdk 12
jdk 11
release
12. 13
Time-Based Release Versioning
A release every 6 months
March 2014: Java 8 LTS
Sept 2017: Java 9
March 2018: Java 10
Sept 2018: Java 11 LTS
March 2019: Java 12
Sept 2019: Java 13
...
March 2021: Java 16
Sept 2021: Java 17 LTS
–
13. 14
Java Community Process
JSR 365: Contexts and Dependency Injection 2.0
JSR 376: Java Platform Module System (several JEPs)
Umbrella JSR 383: Java SE 10
Umbrella JSR 384: Java SE 11
Time based release → Inversion of Control!
14. 15
How is it Distributed?
Java SE are distributions that pass the TCK
AdoptOpenJDK
Azul Zulu / Azul Zing
+ C4 (GC) + Falcon (JIT)
IBM J9
OpenJ9 (VM + JIT + GCs)
OpenJDK
Oracle
+ Mission Control, + Flight Recorder (open sourced!)
RedHat
+ Shenandoah (GC)
18. 20
Local Variable Type Inference
Let the compiler infers the type of local variables
var list = List.of("hello", "devoxx");
var max = list.stream().max(String::compareTo);
max.ifPresent(System.out::println);
Fields and methods can not use var
class Foo {
var foo; // fails
int foo(var a); // fails
}
19. 21
Var & Inference
Works with for(:) and try(=)
for (var element: list) {
...
}
try (var lines = Files.lines(path)) {
...
}
Works beautifully with anonymous classes
var counter = new Object() {
int value;
};
list.forEach(e -> counter.value++);
20. 22
Var & Inference (2)
Inference fails for
var a; // fails
var a = null; // fails
var lambda = x -> x; // fails
var methodref = Integer::sum; // fails
var array = { 1, 2, 3 }; // fails
Work surprisingly if return type is needed
var empty = List.of(); // List<Object>
21. 23
When to use var?
Guidelines
Choose variable names that provide useful information.
Consider var when the initializer provides sufficient
information to the reader.
Don't worry too much about "programming to the interface"
with local variables.
http://openjdk.java.net/projects/amber/LVTIstyle.html
26. 28
var as Type in Lambda Parameters
Lambda parameters can use var
IntBinaryOperator fun = (var x, var y) -> x + y;
Allow modifiers and annotations
IntBinaryOperator fun =
(final var x, final var y) -> x + y;
BinaryOperator<Integer> fun =
(@NonNull var x, @NonNull var y) -> x + y;
28. 31
Can You Spot the Problem?
The compiler does stupid things behind your back
interface Foo {
public int value();
private static int helperValue() { return 42; }
public static Foo create() {
return new Foo() {
public int value() { return helperValue(); }
};
}
}
29. 32
The Accessor is Public!
The inner class needs to access Foo.helperValue()
public interface Foo {
public abstract int value();
private static int helperValue();
Code:
0: bipush 42
2: ireturn
public static int access$000();
Code:
0: invokestatic InterfaceMethod helperValue:()I
3: ireturn
}
30. 33
Can You Spot The Problem (2)?
And what about using reflection?
public class Foo {
private int value;
public class Bar {
public int getValue() {
return Foo.class.getDeclaredField("value")
.getInt();
}
}
}
32. 35
NestMates
Sync the JVMS and the JLS view on private
Creates two new class attributes
NestMembers (in enclosing class)
List all internal classes
NestHost (in internal classes)
Name the enclosing class
private access check by the VM
Is it the same class ?
Is the class listed as a nest members of the nest host ?
33. 36
Class Loading Order?
May the VM load more classes than before?
class Foo {
private static int value;
static class Bar {
int m() { return value; }
int m2() { return 42; }
}
}
No, the access resolution is lazy
but IllegalAccessError may appear later
35. 38
Constant Dynamic
The VM only allow primitives, String, etc. as constant pool constants
add arbitrary constant in the constant pool
Constant pool constant (use ldc to load)
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
Bootstrap method (called once)
Object bsm(Lookup lookup, String name, Class<?> type, ...) {
...
}
36. 39
Example in Java (maybe)
Alleviate the burden of the static block if the initialization
is idempotent
class Foo {
private static final lazy boolean DEBUG =
Boolean.valueOf(System.getenv("DEBUG"));
void bar() {
if (DEBUG) { ... }
}
}
37. 40
Example in Java (maybe)
No static block needed, lazy initiailization done by the VM
class Foo {
10: condy constantMetaFactory.bsm $init_DEBUG$
private static boolean $init_DEBUG$() {
return Boolean.valueOf(System.getenv("DEBUG"));
}
void bar() {
ldc 10
ifeq ...
}
}
38. 41
Constant Lambda
All Lambdas are initialized with indy
class Foo {
IntSupplier provider() {
return () -> 42;
}
}
class Foo {
10: indy LambdaMetaFactory.lambda
$lambda$ IntSupplier()
IntSupplier provider() {
indy 10
}
private static int $lambda$() {
return 42;
}
}
39. 42
Constant Lambda with condy
Constant Lambdas should be initialized with condy
class Foo {
IntSupplier provider() {
return () -> 42;
}
}
class Foo {
10: condy lambdaMetaFactory.condy
$lambda$ IntSupplier
IntSupplier provider() {
ldc 10
}
private static int $lambda$() {
return 42;
}
}
40. 43
Constant Dynamic vs Invokedynamic
Differences between condy and indy
Constant Dynamic InvokeDynamic
can represent only constant any expression
creation lazy created (ldc) lazy created (invokedynamic)
interpreter constant non-constant
c1 constant non-constant
c2 constant constant
graal constant constant
41. 44
Lambda Creation
Java 8
All lambdas creation use invokedynamic
Constant lambdas are constant at JIT time
Java 11
Introduction of constant dynamic in the VM
Java 12?
Constant lambdas use constant dynamic
Need re-compilation :(
Constant lambdas are constant at interpreter time
42. 45
Constant Dynamic and Annotation
Annotation values are constants in the constant pool
Condy allows to define any constants !
Proposal: allow any class instance
as an annotation value
Problem: any use cases?
45. 48
Java String Issues
No way to create a non-despecialized string
var pattern = Pattern.compile("");
Not easy to create a multi-lines string
var s = "this is a longn" +
"multi-linesn" +
"of text";
Not easy to embed code
var s = "<a href="javascript: alert(‘hello’)">";
46. 49
Raw String
Add non-despecialized string and multi-lines string
Starts by n-backticks (`) and ends by n-backticks
var pattern = Pattern.compile(``);
var s = `this is a long
multi-lines
of text`;
var s = ```<a href="javascript: alert(‘hello’)">```;
47. 50
Raw String API
strip(): same as trim(), works with UTF-8 spaces!
stripIndent(): removes indenting spaces
stripLeading(), stripTrailing(): removes
leading and trailing spaces
stripMarkers(): removes what is outside the
markers
lines(): streams the lines of the multiline
48. 51
WARNING!
Because you can embed Java code in raw String
var text =
``
var s = `this is a long
multi-lines
of text`;
``;
it means that `` is not the empty String but the start of
a raw String
49. 52
var local type var inference
10
11 var as lambda parameters raw string nestmatescondy
inner classes
private in VM
53. 56
Limitation of Switch
Switch doesn’t work well when the result is an expression
Car car;
switch(text) {
case "upgrade":
case "awesome":
car = new Mustang();
break;
case "regular":
car = new Clio();
break;
default:
throw new WTFException();
}
54. 57
Switch Expression
Use break expression to specify the result of a case
var car = switch(text) {
case "upgrade":
case "awesome":
break new Mustang();
case "regular":
System.out.println("I am a Clio!");
break new Clio();
default:
throw new WTFException();
};
55. 58
Others Limitations
Try to get rid of fall-through
It makes the code unreadable, easy to introduce regression
when refactoring
Try have a story for null
better than throw NPE
Nice to have: a shorter syntax for single expression?
56. 59
Avoid Fall-through
Allow comma separated cases
var car = switch(text) {
case "upgrade", "awesome":
break new Mustang();
case "regular":
System.out.println("I am a Clio!");
break new Clio();
default:
throw new WTFException();
};
57. 60
Allow case null
NPE unless there is a case null
var car = switch(text) {
case "upgrade", "awesome":
break new Mustang();
case "regular":
System.out.println("I am a Clio!");
break new Clio();
case null, default:
throw new WTFException();
};
Allow to mix case: and default:?
58. 61
Retrofit the Statement Switch
Comma separated cases and case null are also
available on the statement switch
Car car;
switch(text) {
case "upgrade", "awesome":
car = new Mustang();
break;
case "regular":
car = new Clio();
break;
case null, default:
throw new WTFException();
}
59. 62
Single Expression Syntax
Use -> instead of : break
var car = switch(text) {
case "upgrade", "awesome" -> new Mustang();
case "regular":
System.out.println("I am a Clio!");
break new Clio();
case null, default -> throw new WTFException();
};
“->” also work for throw?
60. 63
Problem of -> as shorter syntax
-> used here is not the same as the -> used in lambdas
and the syntax -> { } does not work with case:
Alternative syntax
var car = switch(text) {
case "upgrade", "awesome": new Mustang();
case "regular": new Clio();
case null, default: throw new WTFException();
};
61. 64
I suggest you do this poll at Devoxx. Make sure
to wear flame-proof pants!
Brian Goetz
62. 65
Poll!
1. no shorter syntax, break expr is short enough
2. “->” as shorter syntax
case "regular" -> new Clio();
case "regular": break new Clio();
63. 66
Poll!
1. no shorter syntax, break expr is short enough
2. “->” as shorter syntax
3. “:” as shorter syntax
case "regular" -> new Clio();
case "regular": break new Clio();
case "regular": new Clio();
67. 70
Current Translation Strategy of Switch on String
in pseudo bytecode
aload 0
invokevirtual String.hashCode ()I
lookupswitch
label -231171556 // "upgrade".hashCode()
test.equals("upgrade")? 0: -1
label 1086463900 // "regular".hashCode()
test.equals("regular")? 1: -1
tableswitch
label 0
… // new Mustang
label 1
… // new Clio
istore 1
var car = switch(text) {
case "upgrade": new Mustang();
case "regular": new Clio();
};
68. 71
Current Translation Strategy of Switch
What if the compiler world and the VM world are not
aligned anymore?
For String:
The solution could be to specify that String.hashCode() can
not be changed
For Enum:
It should state that you can only add enum values at the end
of the list
69. 72
New Translation to Bytecode
invokedynamic is used to associate an int to each case
in pseudo-bytecode
invokedynamic (String)I SwitchMetaFactory.string
["upgrade", "awesome", "regular"], [0, 0, 1]
tableswitch
label 0:
… // new Mustang
label 1:
… // new Clio
astore 1
var car = switch(text) {
case "upgrade": new Mustang();
case "regular": new Clio();
};
70. 73
Performance??
Test avec JMH
old switch new switch cascade
if equals
small
(4 cases)
23.9 +/- 0.2 ns/op 11.6 +/- 0.1 ns/op 16.4 +/- 0.1 ns/op
big
(10 cases)
135.4 +/- 4.0 ns/op 86.2 +/- 1.5 ns/op 25.1 +/- 0.4 ns/op
71. 74
New Translation to Bytecode
Switch on enums, String, double, float, long use indy
The bytecode becomes independent from the compiler world
Usually better performance
Less bytecodes
Dynamic strategies adaptation
But it needs to recompile the old code...
And it puts more pressure on the JIT
It may take longer to reach steady state
73. 76
Extending Switch to Accept Types
Computing something on a hierarchy you do not control
int computeTax(Vehicle v) {
if (v instanceof Bike) {
return 10;
} else if (v instanceof Car) {
return 42 + ((Car) v).passengers() * 10;
} else if (v instanceof Bus) {
return ((Bus) v).weight() * ((Bus) v).wheels();
} else {
throw new WTFException("unknown vehicle kind");
}
}
74. 77
Extending Switch to Accept Types
One can use a Visitor (if there is a method accept)
int computeTax(Vehicle v) {
return v.accept(VISITOR);
}
private static final Visitor VISITOR = new Visitor() {
public int visitBike(Bike bike) { return 10; }
public int visitCar(Car car) {
return 42 + car.passengers() * 10;
}
public int visitBus(Bus bus) {
return bus.weight() * bus.wheels();
}
};
75. 78
Extending Switch to Accept Types
Computing something on a hierarchy you do not control
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car: 42 + ((Car) v).passengers() * 10;
case Bus: ((Bus) v).weight() * ((Bus) v.wheels();
default: throw new WTFException("unknown ...");
};
}
76. 79
Translation to Bytecode
Same Translation as with Strings
aload 0
invokedynamic (Vehicle)I SwitchMetaFactory.types
[Bike.class, Car.class, Bus.class], [0, 1, 2]
tableswitch
label 0:
bipush 10
label 1:
bipush 42
aload 0
checkcast Car
invokevirtual Car passagers ()I
iadd
label 2:
… // ((Bus) v).weight() * ((Bus) v).wheels()
ireturn
77. 80
Performance??
Test with JMH
instanceof type switch
lot of cases
lot of classes
395 +/- 21.2 ns/op 55 +/- 2.1 ns/op
small n cases
lot of classes
35 +/- 1.6 ns/op 51 +/- 0.7 ns/op
small n cases
small n classes
7 +/- 0.1 ns/op 6 +/- 0.1 ns/op
78. 81
Extending Switch to Accept Types
Naming the type avoid the cast
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car car: 42 + car.passengers() * 10;
case Bus bus: bus.weight() * bus.wheels();
default: throw new WTFException("unknown ...");
};
}
79. 82
Bringing Back the Encapsulation
Allows destructured switch on type
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car(int passengers): 42 + passengers * 10;
case Bus(int weight, int wheels): weight * wheels;
...
};
}
We need the inverse operation of a constructor
80. 83
De-constructor
A de-constructor describes how to get a tuple of values
from an object
class Bus {
Bus(int weight, int wheels) { … }
(int, int) deconstructor() { return …; }
}
But returning several values in not easy in Java (yet!)
81. 84
A Path to a Solution
The problem can be solved for classes with a primary
constructor
record Bus(int weight, int wheels) {
int weight; // compiler generated
int wheels; // compiler generated
public static Extractor extractor() { // compiler generated
return Extractor.of(bus -> bus.weight, bus -> bus.wheels);
}
}
The Extractor is then a list of getters
82. 85
Switch on Records
record Bike implements Vehicle
record Car(int passengers) implements Vehicle
record Bus(int weight, int wheels)
A record provides an extractor that returns its component values
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car(var passengers): 42 + passengers * 10;
case Bus(var weight, var wheels): weight * bus.wheels;
default: throw new WTFException("unknown vehicle kind");
};
}
83. 86
Switch on Records - Translation
The getter from the extractor are defined as Constant Dynamic
0: ConstantDynamic MethodHandle SwitchMetafactory.getter [Car.class, 0];
1: ConstantDynamic MethodHandle SwitchMetafactory.getter [Bus.class, 0];
2: ConstantDynamic MethodHandle SwitchMetafactory.getter [Bus.class, 1];
int computeTax(Vehicle v) {
aload 0
invokedynamic (Vehicle)I SwitchMetaFactory.types
[Bike.class, Car.class, Bus.class] [0, 1, 2]
tableswitch
label 0:
bipush 10
label 1:
bipush 42
aload 0
invokedynamic (Vehicle)I SwitchMetafactory.extract [item 0]
iadd
label 2:
…
ireturn 1
85. 88
Record
A plain simple data object
record Point(final int x, final int y)
is translated to
public final class Point {
final int x;
final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
public int x() { return x; }
public int y() { return y; }
// + equals / hashCode / toString / extractor
}
86. 89
Record
Record can have supplementary methods but no
supplementary fields
record Point(final int x, final int y)
record Circle(final Point center, final int radius) {
public double surface() {
return Math.PI * radius * radius;
}
}
87. 90
Record and Precondition
Relax java rule: you can have preconditions before the calls
to the super / default constructor
record Circle(final Point center, final int radius) {
public Circle(Point center, int radius) {
Preconditions.requiresNonNull(center);
Preconditions.requiresPositive(radius);
default(center, radius);
}
}
java.util.Preconditions contains usual precondition tests
88. 91
Setters are not Generated!
Because the compiler doesn’t know about preconditions
record Circle(final Point center, final int radius) {
public Circle(Point center, int radius) {
requiresNonNull(center);
requiresPositive(radius);
default(center, radius);
}
public void center(Point center) {
this.center = requiresNonNull(center);
}
public void radius(int radius) {
this.radius = requiresPositive(radius);
}
}
90. 93
equals / hashCode / toString / extractor
The code is not generated by the compiler
Use invokedynamic + constant dynamic
/*record*/ final class Bus {
10: constant dynamic Extractor RecordMetafactory.extractor
[Bus::weight, Bus::wheels]
final int weight;
final int wheels;
public String toString() {
return invokedynamic RecordMetafactory.toString
[constant item 10]
}
}
91. 94
Indy + Condy
The Extractor is computed once lazily
equals(), balance the cascade of if … else depending on
the cost of equals() for each components
can re-balance the cascade if..else at runtime!
equals and hashCode can do nullcheck implicitly
92. 95
Extractor
Use ldc + constant dynamic
/*record*/ final class Bus {
10: constant dynamic Extractor RecordMetafactory.extractor
[Bus::weight, Bus::wheels]
final int weight;
final int wheels;
public static /*extractor*/ Extractor extractor() {
ldc [constant item 10]
areturn
}
}
93. 96
Record vs Property
Is Record + Extractor like Rémi / Stephen Colebourne
Property Proposal written 10 years ago!
shhhhhhhhhhhhhh!
94. 97
var local type var inference
10
11 var as lambda parameters raw string nestmatescondy
12 - 13
inner classes
private in VM
expr switch
type switch
const lambda
record
98. 101
Exhaustive Switch??
record Bike() implements Vehicle
record Car(final int passengers) implements Vehicle
record Bus(final int weight, final int wheels) implements Vehicle
How can we remove this pesky default ?
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car(var passengers): 42 + passengers * 10;
case Bus(var weight, var wheels): weight * bus.wheels;
default: throw new WTFException("unknown vehicle kind");
};
}
99. 102
Answer: Sealed Interface!
Close the interface by listing all the subtypes
sealed interface Vehicle {
record Bike implements Vehicle
record Car(...) implements Vehicle
record Bus(...) implements Vehicle
}
Implemented using NestMates + flag on the interface
100. 103
Sealed Interface Enables Exhaustive Switch
The compiler knows that all the subtypes are covered
int computeTax(Vehicle v) {
return switch(v) {
case Bike: 10;
case Car(var passengers): 42 + passengers * 10;
case Bus(var weight, var wheels): weight * wheels;
};
}
How does the VM know that the switch is exhaustive?
101. 104
Sealed Interface Enables Exhaustive Switch
How does the VM know that the switch is exhaustive?
sealed interface Vehicle {
record Bike implements Vehicle
record Car(...) implements Vehicle
record Bus(...) implements Vehicle
}
Because the subtypes are nestmates!
103. 106
Generalized Pattern Matching
Constants are allowed
Can extract value from different objects
“_” means whatever
var value = switch(shape) {
case Circle(_, 0): 0;
case Circle(_, var radius): PI*radius*radius;
case Rectangle(Point(var x1, var y1), Point(var x2, var y2)):
abs(x2 – x1)*abs(y2 – y1);
}
106. 110
Problems
Time ratio to read a value in a CPU register vs RAM
circa 1990: 1 to 1
nowadays: 1 to 100
The cost of cache misses dramatically increases!
Modern programming => more abstractions
=> JITs are more powerful
but the programming model prevents JITs to deliver
zero cost abstraction
107. 111
Value Type
Writes like a class, works like an int
No reference, only direct value
No identity, == returns false
System.identityHashCode(), synchronized, wait()
all this fails
No need to be allocated on the heap
108. 112
The Value Type Color
A value type has fields and methods
value class Color {
final int red;
final int green;
final int blue;
public static Color <create>() { … }
public int red() { return red; }
}
A value type is immutable!
110. 114
An Array of Color Instances
header
With references With value types
red
blue
green
header
red
blue
green
header
red
blue
green
header
red
blue
green
red
blue
green
red
blue
green
111. 117
Performance!
Value types
More pressure on the JITs (never escape)
Less pressure to the GCs
JMH on an array of Color
1 000 000
of colors
creation time
(write)
calculation time
(read)
jdk10 97.2 ms 52.5 ms
jdk10-mvt 30.5 ms 18.5 ms
112. 118
Value Based Class and Compatibility
Since 8, some classes are documented as
value based classes
Beware, they will becomes value types in the future
113. 119
var local type var inference
10
11 var as lambda parameters raw string nestmatescondy
12 - 13
inner classes
private in VM
expr switch
type switch
const lambda
record
14+ sealed interface
pattern matching
value type