Alf
Alf

Reputation: 1538

Why is there a difference between using `field(select(...))` and `select(...).asField()`?

In the following code, my intuition is that the variables x and y get the same type:

var x = field(select(T.A).from(T));
var y = select(T.A).from(T).asField();

If I ask IntelliJ to replace var with explicit type I get:

Field<String> x = field(select(T.A).from(T));
Field<Object> y = select(T.A).from(T).asField();

I trust IntelliJ over my intuition, so now I wonder why. Is this a limitation in Java? A bug in jOOQ? Something else?

Here's the method signatures for field and asField for reference:

public static <T> Field<T> field(Select<? extends Record1<T>> select);
public <T> Field<T> asField();

Upvotes: 1

Views: 28

Answers (2)

Jorge Tabuenca
Jorge Tabuenca

Reputation: 1

To ensure y has the correct type (Field), you can explicitly specify the type parameter for asField():

Field<String> y = select(T.A).from(T).<String>asField();
  • The difference arises because field(...) leverages the type context of its argument, while asField() must infer its type independently.
  • This is neither a bug in jOOQ nor a limitation in Java but an expected outcome of Java's type inference rules.
  • To align types, explicitly specify the type parameter for asField() or ensure that the context provides enough information for type inference.

Upvotes: 0

Lukas Eder
Lukas Eder

Reputation: 220867

DSL.field() is a generic method that can capture its type variable T. FieldLike.asField() cannot capture a type variable based on whatever subtype of FieldLike you're using, including Select<R>. Due to a historic mistake, it's still a generic record, which allows you to unsafely cast the T type at the use-site. But that's just an unsafe cast, not a correct capture of your desired type String

There's no way the asField() method could be fixed in Java as the jOOQ API is designed right now, because the type variable R in Select<R> is not aware of the fact whether it is effectively a Record1<T> (a scalar subquery), or anything else.

Other languages can add additional "constraints" on their generic type bounds, or they allow for using extension methods to give the feeling of a .asField() "method", which in fact is really just a static function like DSL.field(), but with a suffix notation on their first parameter.

The only valid solution in Java would have been ot "overload" the Select type with:

  • Select1<T1, R extends Record1<T1>> extends Select<R>
  • Select2<T1, T2, R extends Record2<T1, T2>> extends Select<R>
  • Select3<T1, T2, T3, R extends Record3<T1, T2, T3>> extends Select<R>
  • ...

But that would have tons of additional, undesirable side effects thoughout the jOOQ API, which is why the idea was rejected and DSL.field() was introduced.

Logically, the two methods are equivalent, but DSL.field() is superior because it captures the correct type.

Upvotes: 1

Related Questions