Qualifying by Generic Type Arguments
Since Micronaut 3.0, it is possible to select which bean to inject based on the generic type arguments of the class or interface. Consider the following example:
public interface CylinderProvider {
int getCylinders();
}
interface CylinderProvider {
int getCylinders()
}
interface CylinderProvider {
val cylinders: Int
}
The CylinderProvider
interface provides the number of cylinders.
public interface Engine<T extends CylinderProvider> { (1)
default int getCylinders() {
return getCylinderProvider().getCylinders();
}
default String start() {
return "Starting " + getCylinderProvider().getClass().getSimpleName();
}
T getCylinderProvider();
}
interface Engine<T extends CylinderProvider> { (1)
default int getCylinders() { cylinderProvider.cylinders }
default String start() { "Starting ${cylinderProvider.class.simpleName}" }
T getCylinderProvider()
}
interface Engine<T : CylinderProvider> { (1)
val cylinders: Int
get() = cylinderProvider.cylinders
fun start(): String {
return "Starting ${cylinderProvider.javaClass.simpleName}"
}
val cylinderProvider: T
}
1 | The engine class defines a generic type argument <T> that must be an instance of CylinderProvider |
You can define implementations of the Engine
interface with different generic type arguments. For example for a V6 engine:
public class V6 implements CylinderProvider {
@Override
public int getCylinders() {
return 7;
}
}
class V6 implements CylinderProvider {
@Override
int getCylinders() { 7 }
}
class V6 : CylinderProvider {
override val cylinders: Int = 7
}
The above defines a V6
class that implements the CylinderProvider
interface.
@Singleton
public class V6Engine implements Engine<V6> { (1)
@Override
public V6 getCylinderProvider() {
return new V6();
}
}
@Singleton
class V6Engine implements Engine<V6> { (1)
@Override
V6 getCylinderProvider() { new V6() }
}
@Singleton
class V6Engine : Engine<V6> { (1)
override val cylinderProvider: V6
get() = V6()
}
1 | The V6Engine implements Engine providing V6 as a generic type parameter |
And a V8 engine:
public class V8 implements CylinderProvider {
@Override
public int getCylinders() {
return 8;
}
}
class V8 implements CylinderProvider {
@Override
int getCylinders() { 8 }
}
class V8 : CylinderProvider {
override val cylinders: Int = 8
}
The above defines a V8
class that implements the CylinderProvider
interface.
@Singleton
public class V8Engine implements Engine<V8> { (1)
@Override
public V8 getCylinderProvider() {
return new V8();
}
}
@Singleton
class V8Engine implements Engine<V8> { (1)
@Override
V8 getCylinderProvider() { new V8() }
}
@Singleton
class V8Engine : Engine<V8> { (1)
override val cylinderProvider: V8
get() = V8()
}
1 | The V8Engine implements Engine providing V8 as a generic type parameter |
You can then use the generic arguments when defining the injection point and Micronaut will pick the correct bean to inject based on the specific generic type arguments:
@Inject
public Vehicle(Engine<V8> engine) {
this.engine = engine;
}
@Inject
Vehicle(Engine<V8> engine) {
this.engine = engine
}
@Singleton
class Vehicle(val engine: Engine<V8>) {
In the above example the V8Engine
bean is injected.