The type of “o” is non denotable, we cannot give it a name (we could uselessly assign it to an Object variable, though). But the new “var” keyword can “capture” it (my wording) to make it usable within a local scope. This could already be done prior to Java 10, when chaining methods (or attribute references).
A rarely used feature are methods in anonymous classes that do not override / implement a super type’s method. They are available only in a very narrow scope. Prior to Java 10, we could only call either m() or n() on such a class, but not both, using the following syntax:
(new Object() {
void m() {
System.out.println("m");
}
void n() {
System.out.println("n");
}
}).m();
// Now, how to call n()?
So, again, this is like “chaining methods”, where the m() call is chained to the constructor call.
The language feature of adding methods to anonymous classes wasn’t too useful. Only one method could be called from the “outside” of the anonymous class, as the instance reference will have gone quickly. With Java 10, we can assign the whole expression to a local variable, without losing the anonymous type.
On a side-note, Java always had a funky and weird love-hate relationship with structural typing, trying to be a mostly nominally typed language. Yet, as we can see in this example, another new kind of structural type has snuck into the language. Cool!
What does this mean for jOOQ?
jOOQ has some cool types. Just look at the API:
Ultimately, depending on how many columns you want to project in your SELECT statement, you’ll get a different Record[N]<T1, T2, ..., T[N]> type, e.g.
What’s nice is the fact that there is record-level type safety, i.e. you know that the record has 3 columns and that they’re all of type String. What’s less nice is that in order to profit from this type safety, you have to actually write down the type, which can get laborious (both when writing and when reading it), e.g. when you select 16 columns or more.
Java 10 changes this. It’s now possible to simply write
for (var r : using(con)
.select(c.TABLE_SCHEMA, c.TABLE_NAME, c.COLUMN_NAME)
.from(c))
System.out.println(
r.value1() + "." + r.value2() + "." + r.value3());
I.e. using the keyword “var” (or “final var”, if you prefer) to create the loop variable. And it will still be type safe. For instance, you cannot call r.value4() on it:
This isn’t a game changer, but for folks coming from Kotlin or Scala, it is a big relief to see that this option is now given to Java developers too.
And this isn’t just useful for results in jOOQ. You can also use it for creating dynamic SQL, e.g.:
// Create a subquery listing all tables called TABLES in any schema
var subq = select(t.TABLE_SCHEMA, t.TABLE_NAME)
.from(t)
.where(t.TABLE_NAME.eq("TABLES"));
// Create a predicate that uses the above subquery:
var pred = row(c.TABLE_SCHEMA, c.TABLE_NAME).in(subq);
// use the above predicate in an actual query
var q = using(con).selectFrom(c).where(pred);
So, clearly, this is going to be a really really useful Java release for jOOQ folks.
So instead of letting the IDE write the explicit type (easy, has no upper bound) I get to mentally keep track of what returns what (hard, bounded by the weakest link – the human working memory). Especially juicy when you see the code for the first time and have no clue what a method returns. Brilliant!
You’re overengineering problems where there might be none. For instance, the same issue arises when using fluent APIs like the Stream API (is it a Stream<Integer> or a Stream<String> or a Stream<List<Object>>?), or when using lambdas.
You can always opt in to explicitly declaring types when that works better, but sometimes, that’s just noise.
Get over it, and embrace the future :) (or simply don’t use the feature)
shouldn’t this…
var pred = row(c.TABLE_SCHEMA, c.TABLE_NAME).in(sub1);
be
var pred = row(c.TABLE_SCHEMA, c.TABLE_NAME).in(subq);
(sub1 -> subq)
Yeah, nice catch. Fixed, thanks!
So instead of letting the IDE write the explicit type (easy, has no upper bound) I get to mentally keep track of what returns what (hard, bounded by the weakest link – the human working memory). Especially juicy when you see the code for the first time and have no clue what a method returns. Brilliant!
You’re overengineering problems where there might be none. For instance, the same issue arises when using fluent APIs like the Stream API (is it a Stream<Integer> or a Stream<String> or a Stream<List<Object>>?), or when using lambdas.
You can always opt in to explicitly declaring types when that works better, but sometimes, that’s just noise.
Get over it, and embrace the future :) (or simply don’t use the feature)