How can I create DI bindings for generic types in Kotlin?
I'm trying to find a Kotlin-based dependency injection system that will allow me to bind generic types like Foo<T> and resolve Foo<Int>, Foo<String>, etc. as needed.
Kodein seems to be one of the more popular DI frameworks and I've looked into that, but don't see how it's possible. Issue #83 in the Kodein repository has some discussion around this topic.
In my case, I'd like to be able to do something like this:
import com.atpgroup.testmaster.domain.ExecutableNode
import kotlin.reflect.KClass
annotation class TypeClass
annotation class Instance
/**
* Represents a catalog of nodes.
*
* Factory functions to generate nodes are initially registered to this catalog during setup and used to create
* instances of nodes.
*/
interface NodeCatalog {
fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U
}
/**
* Simplifies resolution of nodes from a reified context.
*/
inline fun <reified T : ExecutableNode> NodeCatalog.resolve(id: String) = resolve(T::class, id)
/**
* Dummy implementation of a node catalog.
*/
class DummyNodeCatalog : NodeCatalog {
override fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U = TODO("not implemented")
}
/**
* A type class representing the set of types that can behave like numbers.
*/
@TypeClass
interface Num<T>
// Int and Double both behave like numbers.
@Instance object IntNumInstance : Num<Int>
@Instance object DoubleNumInstance : Num<Double>
/**
* An executable node that adds two numbers together.
*
* @param T the type of the numbers that will be added together
*/
class AddNode<T>(override val id: String, private val N: Num<T>) : ExecutableNode {
override suspend fun execute() = TODO("not implemented")
}
// Resolve two "add nodes" that operate on different types of numbers.
val catalog = DummyNodeCatalog()
val addInts = catalog.resolve<AddNode<Int>>("integer adder")
val addDoubles = catalog.resolve<AddNode<Double>>("double adder")
In C# this is pretty straightforward using Autofac. The snippet below is a working implementation in C# of what I'd like:
using System;
using Autofac;
namespace Experiment
{
internal class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<IntNumInstance>().As<INum<int>>();
builder.RegisterType<DoubleNumInstance>().As<INum<double>>();
builder.RegisterGeneric(typeof(Add<>));
var container = builder.Build();
var addA = container.Resolve<Add<int>.Factory>()("add integers");
var addB = container.Resolve<Add<double>.Factory>()("add doubles");
Console.WriteLine(addA);
Console.WriteLine(addB);
Console.ReadLine();
}
}
interface INum<T> {}
class IntNumInstance : INum<int> {}
class DoubleNumInstance : INum<double> {}
class Add<T>
{
public delegate Add<T> Factory(string id);
public Add(string id, INum<T> N)
{
Id = id;
this.N = N;
}
public string Id { get; }
private INum<T> N { get; }
public override string ToString() => $"Add Node ({Id} - {N})";
}
}
Output
Add Node (add integers - Experiment.IntNumInstance)
Add Node (add doubles - Experiment.DoubleNumInstance)
generics dependency-injection kotlin
add a comment |
I'm trying to find a Kotlin-based dependency injection system that will allow me to bind generic types like Foo<T> and resolve Foo<Int>, Foo<String>, etc. as needed.
Kodein seems to be one of the more popular DI frameworks and I've looked into that, but don't see how it's possible. Issue #83 in the Kodein repository has some discussion around this topic.
In my case, I'd like to be able to do something like this:
import com.atpgroup.testmaster.domain.ExecutableNode
import kotlin.reflect.KClass
annotation class TypeClass
annotation class Instance
/**
* Represents a catalog of nodes.
*
* Factory functions to generate nodes are initially registered to this catalog during setup and used to create
* instances of nodes.
*/
interface NodeCatalog {
fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U
}
/**
* Simplifies resolution of nodes from a reified context.
*/
inline fun <reified T : ExecutableNode> NodeCatalog.resolve(id: String) = resolve(T::class, id)
/**
* Dummy implementation of a node catalog.
*/
class DummyNodeCatalog : NodeCatalog {
override fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U = TODO("not implemented")
}
/**
* A type class representing the set of types that can behave like numbers.
*/
@TypeClass
interface Num<T>
// Int and Double both behave like numbers.
@Instance object IntNumInstance : Num<Int>
@Instance object DoubleNumInstance : Num<Double>
/**
* An executable node that adds two numbers together.
*
* @param T the type of the numbers that will be added together
*/
class AddNode<T>(override val id: String, private val N: Num<T>) : ExecutableNode {
override suspend fun execute() = TODO("not implemented")
}
// Resolve two "add nodes" that operate on different types of numbers.
val catalog = DummyNodeCatalog()
val addInts = catalog.resolve<AddNode<Int>>("integer adder")
val addDoubles = catalog.resolve<AddNode<Double>>("double adder")
In C# this is pretty straightforward using Autofac. The snippet below is a working implementation in C# of what I'd like:
using System;
using Autofac;
namespace Experiment
{
internal class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<IntNumInstance>().As<INum<int>>();
builder.RegisterType<DoubleNumInstance>().As<INum<double>>();
builder.RegisterGeneric(typeof(Add<>));
var container = builder.Build();
var addA = container.Resolve<Add<int>.Factory>()("add integers");
var addB = container.Resolve<Add<double>.Factory>()("add doubles");
Console.WriteLine(addA);
Console.WriteLine(addB);
Console.ReadLine();
}
}
interface INum<T> {}
class IntNumInstance : INum<int> {}
class DoubleNumInstance : INum<double> {}
class Add<T>
{
public delegate Add<T> Factory(string id);
public Add(string id, INum<T> N)
{
Id = id;
this.N = N;
}
public string Id { get; }
private INum<T> N { get; }
public override string ToString() => $"Add Node ({Id} - {N})";
}
}
Output
Add Node (add integers - Experiment.IntNumInstance)
Add Node (add doubles - Experiment.DoubleNumInstance)
generics dependency-injection kotlin
and what exactly does not work for you? The DI of the differentExecutableNodes?
– Lino
Nov 21 at 13:43
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
Or in other words - how do I create a working implementation ofNodeCatalog?
– Tagc
Nov 21 at 13:47
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10
add a comment |
I'm trying to find a Kotlin-based dependency injection system that will allow me to bind generic types like Foo<T> and resolve Foo<Int>, Foo<String>, etc. as needed.
Kodein seems to be one of the more popular DI frameworks and I've looked into that, but don't see how it's possible. Issue #83 in the Kodein repository has some discussion around this topic.
In my case, I'd like to be able to do something like this:
import com.atpgroup.testmaster.domain.ExecutableNode
import kotlin.reflect.KClass
annotation class TypeClass
annotation class Instance
/**
* Represents a catalog of nodes.
*
* Factory functions to generate nodes are initially registered to this catalog during setup and used to create
* instances of nodes.
*/
interface NodeCatalog {
fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U
}
/**
* Simplifies resolution of nodes from a reified context.
*/
inline fun <reified T : ExecutableNode> NodeCatalog.resolve(id: String) = resolve(T::class, id)
/**
* Dummy implementation of a node catalog.
*/
class DummyNodeCatalog : NodeCatalog {
override fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U = TODO("not implemented")
}
/**
* A type class representing the set of types that can behave like numbers.
*/
@TypeClass
interface Num<T>
// Int and Double both behave like numbers.
@Instance object IntNumInstance : Num<Int>
@Instance object DoubleNumInstance : Num<Double>
/**
* An executable node that adds two numbers together.
*
* @param T the type of the numbers that will be added together
*/
class AddNode<T>(override val id: String, private val N: Num<T>) : ExecutableNode {
override suspend fun execute() = TODO("not implemented")
}
// Resolve two "add nodes" that operate on different types of numbers.
val catalog = DummyNodeCatalog()
val addInts = catalog.resolve<AddNode<Int>>("integer adder")
val addDoubles = catalog.resolve<AddNode<Double>>("double adder")
In C# this is pretty straightforward using Autofac. The snippet below is a working implementation in C# of what I'd like:
using System;
using Autofac;
namespace Experiment
{
internal class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<IntNumInstance>().As<INum<int>>();
builder.RegisterType<DoubleNumInstance>().As<INum<double>>();
builder.RegisterGeneric(typeof(Add<>));
var container = builder.Build();
var addA = container.Resolve<Add<int>.Factory>()("add integers");
var addB = container.Resolve<Add<double>.Factory>()("add doubles");
Console.WriteLine(addA);
Console.WriteLine(addB);
Console.ReadLine();
}
}
interface INum<T> {}
class IntNumInstance : INum<int> {}
class DoubleNumInstance : INum<double> {}
class Add<T>
{
public delegate Add<T> Factory(string id);
public Add(string id, INum<T> N)
{
Id = id;
this.N = N;
}
public string Id { get; }
private INum<T> N { get; }
public override string ToString() => $"Add Node ({Id} - {N})";
}
}
Output
Add Node (add integers - Experiment.IntNumInstance)
Add Node (add doubles - Experiment.DoubleNumInstance)
generics dependency-injection kotlin
I'm trying to find a Kotlin-based dependency injection system that will allow me to bind generic types like Foo<T> and resolve Foo<Int>, Foo<String>, etc. as needed.
Kodein seems to be one of the more popular DI frameworks and I've looked into that, but don't see how it's possible. Issue #83 in the Kodein repository has some discussion around this topic.
In my case, I'd like to be able to do something like this:
import com.atpgroup.testmaster.domain.ExecutableNode
import kotlin.reflect.KClass
annotation class TypeClass
annotation class Instance
/**
* Represents a catalog of nodes.
*
* Factory functions to generate nodes are initially registered to this catalog during setup and used to create
* instances of nodes.
*/
interface NodeCatalog {
fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U
}
/**
* Simplifies resolution of nodes from a reified context.
*/
inline fun <reified T : ExecutableNode> NodeCatalog.resolve(id: String) = resolve(T::class, id)
/**
* Dummy implementation of a node catalog.
*/
class DummyNodeCatalog : NodeCatalog {
override fun <U : ExecutableNode> resolve(nodeType: KClass<U>, id: String): U = TODO("not implemented")
}
/**
* A type class representing the set of types that can behave like numbers.
*/
@TypeClass
interface Num<T>
// Int and Double both behave like numbers.
@Instance object IntNumInstance : Num<Int>
@Instance object DoubleNumInstance : Num<Double>
/**
* An executable node that adds two numbers together.
*
* @param T the type of the numbers that will be added together
*/
class AddNode<T>(override val id: String, private val N: Num<T>) : ExecutableNode {
override suspend fun execute() = TODO("not implemented")
}
// Resolve two "add nodes" that operate on different types of numbers.
val catalog = DummyNodeCatalog()
val addInts = catalog.resolve<AddNode<Int>>("integer adder")
val addDoubles = catalog.resolve<AddNode<Double>>("double adder")
In C# this is pretty straightforward using Autofac. The snippet below is a working implementation in C# of what I'd like:
using System;
using Autofac;
namespace Experiment
{
internal class Program
{
public static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<IntNumInstance>().As<INum<int>>();
builder.RegisterType<DoubleNumInstance>().As<INum<double>>();
builder.RegisterGeneric(typeof(Add<>));
var container = builder.Build();
var addA = container.Resolve<Add<int>.Factory>()("add integers");
var addB = container.Resolve<Add<double>.Factory>()("add doubles");
Console.WriteLine(addA);
Console.WriteLine(addB);
Console.ReadLine();
}
}
interface INum<T> {}
class IntNumInstance : INum<int> {}
class DoubleNumInstance : INum<double> {}
class Add<T>
{
public delegate Add<T> Factory(string id);
public Add(string id, INum<T> N)
{
Id = id;
this.N = N;
}
public string Id { get; }
private INum<T> N { get; }
public override string ToString() => $"Add Node ({Id} - {N})";
}
}
Output
Add Node (add integers - Experiment.IntNumInstance)
Add Node (add doubles - Experiment.DoubleNumInstance)
generics dependency-injection kotlin
generics dependency-injection kotlin
asked Nov 21 at 9:13
Tagc
5,01552870
5,01552870
and what exactly does not work for you? The DI of the differentExecutableNodes?
– Lino
Nov 21 at 13:43
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
Or in other words - how do I create a working implementation ofNodeCatalog?
– Tagc
Nov 21 at 13:47
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10
add a comment |
and what exactly does not work for you? The DI of the differentExecutableNodes?
– Lino
Nov 21 at 13:43
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
Or in other words - how do I create a working implementation ofNodeCatalog?
– Tagc
Nov 21 at 13:47
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10
and what exactly does not work for you? The DI of the different
ExecutableNodes?– Lino
Nov 21 at 13:43
and what exactly does not work for you? The DI of the different
ExecutableNodes?– Lino
Nov 21 at 13:43
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
Or in other words - how do I create a working implementation of
NodeCatalog?– Tagc
Nov 21 at 13:47
Or in other words - how do I create a working implementation of
NodeCatalog?– Tagc
Nov 21 at 13:47
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10
add a comment |
active
oldest
votes
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53408628%2fhow-can-i-create-di-bindings-for-generic-types-in-kotlin%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53408628%2fhow-can-i-create-di-bindings-for-generic-types-in-kotlin%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
and what exactly does not work for you? The DI of the different
ExecutableNodes?– Lino
Nov 21 at 13:43
I don't know to configure Kodein (or any other DI container) to be able to resolve generic types with different type arguments. The first code snippet shows how I'd like to be able to resolve generic types, the question I have is how to register bindings with a DI container to permit that.
– Tagc
Nov 21 at 13:46
Or in other words - how do I create a working implementation of
NodeCatalog?– Tagc
Nov 21 at 13:47
Based on discussions in the Kodein Slack channel it seems like this is impossible. I've worked around this by not using Kodein and instead doing the most God-awful and hacky reflection-based source-code-manipulation-during-compilation imaginable. But it works.
– Tagc
Nov 30 at 8:10