Any Java Generics wizards out there feel like taki...
# random
s
Any Java Generics wizards out there feel like taking a stab at explaining what looks like a weird type inference failure? (But might just accidentally be an abuse of the type system) 🧵 ⬇️
This is a mostly-minimal reproduction of the issue. It’s a little hard to explain succinctly, but what I’ve replicated here is a class hierarchy used to set up a service class by giving it a simple POJO with config values in it and passing in configurable submodules (called
Components
here), which also need access to that POJO to perform their own setup. Components are instantiated by appropriate
ComponentSetups
(e.g.
FooComponentSetup
sets up a
FooComponent
) which are parameterized, and those setups enforce the presence of certain config values in the POJO by requiring that the config conforms to a given type constraint/union using its type parameter—the examples here are
ConfigWithFoo
and
ConfigWithBar
. The specifics of how these services are supposed to be bootstrapped isn’t super important, but in the course of using this framework, I’ve run into a weird issue where javac seems to not be able to infer the correct generic type in a particular circumstance, specifically when I try to use a fluent method on a `ComponentSetup`—the method returns itself, and should ostensibly have the exact same generic parameters as the result of the preceding constructor call, but instead, Java infers the generic constraint instead of the concrete class which fulfills that constraint, possibly because it can’t infer a type at all
Some screenshots of the relevant intellij reporting/compiler errors
My first instinct was that maybe this was a type projection issue, that some combination of wildcards and/or `extends`/`super` or maybe another type variable would fix it, but nothing I’ve tried really works unless you do some type erasure. I kinda wonder if this is a compiler bug? Those are pretty rare, though, and the problem happens with Java ≤ 21, possibly even later versions. Frankly, this might just be me misunderstanding how the type system works at a deep level, and maybe I’m trying to do something actually unsound here. If you’ve ever seen this issue, please let me know, especially if there’s an open bug or even just a name for this phenomenon—something I can search for and hopefully understand better.
In terms of maybe doing this better—I didn’t originally write this code, and I probably would have stopped and reevaluated the feasibility of using the type system like this if I had written it. I have a feeling that
ComponentSetup
might not need to be generic, but I haven’t quite sorted out how to keep the type safety without the parameter
d
FooComponentSetup
requires
T
to extend
ServiceConfig & ConfigWithFoo
. Not everything that extends both of these is a
MyServiceConfig
. For example, a
YourServiceConfig
class could be created that extends both of these but does not extend
MyServiceConfig
. So if you type the assignee with
MyServiceConfig
, you must also explicitly type the corresponding instantiation with
MyServiceConfig
(as you do on lines 66 and 72).
Oh, you're asking why the compiler doesn't infer
MyServiceConfig
as the instantiation's type arg the same way it would if you were instantiating a list or whatever. IDK. homer disappear
👆 1