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?

+3


source to share


1 answer


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 ...).

+1


source







All Articles