Inference of parameter types to a generic class, which are nested generic types of supplied arguments
I have a class:
class MyClass<F extends Field, G extends OtherClass<F>> {
...
}
The problem is, given the class information G
, I can easily specify the type F
. However, instantiation now requires me to do this:
new MyClass<ConcreteField, ConcreteOtherClass>();
To use the syntax <>
, I used a little hack and added a constructor:
public MyClass(Class<G> gClass) {}
This allows me to use the diamond syntax as the compiler can now deduce types easily:
new MyClass<>(ConcreteOtherClass.class);
Is there a way for me to only require the type to be passed G
(as in "constructor hackers") without using a constructor as mentioned?
source to share
Thought about this for a while, but I have not found a simple general construction that allows this type of inference.
As for the custom argument-argument constructor, it's worth noting that you don't need to pass the actual instance Class
; any type that can be inferred G
, including G
by itself. For example, if you change the constructor public MyClass(Class<G> gClass) {}
to public MyClass(G ignored) {}
, you can instantiate the class with new MyClass<>((ConcreteOtherClass)null)
.
You can also hide the link null
and write an instance without any type by adding a method that provides the value of the dummy file:
class MyClass<F extends Field, G extends OtherClass<F>> {
public MyClass() {}
public MyClass(G dummy) {}
public static <T> T infer() {
return null;
}
}
Then you can instantiate the class with the full output type like this:
new MyClass<>(MyClass.<ConcreteOtherClass>infer())
Since you asked for a solution without a custom constructor, I'll add a solution like this. But you will see that this does not improve the maintainability of your code:
interface Instantiator<T extends OtherClass<?>> extends Runnable {
default <M extends MyClass<?,? extends T>> M get(Supplier<? extends M> s) {
return s.get();
}
static <X extends OtherClass<?>> Instantiator<X> i() {
return ()->{};
}
}
Using this helper class, you can instantiate MyClass
an output type using the default constructor as
Instantiator.<ConcreteOtherClass>i().get(MyClass::new)
Note that this two-step instance will work with regular types as well, but making this Instantiator
functional interface and creating it through a lambda expression is the only way to make it single-type safe, i.e. with the current Oracles There will only be one instance inside the runtime Instantiator
, no matter how you parameterize it, since it is stateless (and only exists to make the compiler happy ...).
source to share