How can I create DI bindings for generic types in Kotlin?












0














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)









share|improve this question






















  • 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
















0














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)









share|improve this question






















  • 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














0












0








0







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)









share|improve this question













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






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 21 at 9:13









Tagc

5,01552870




5,01552870












  • 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


















  • 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
















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

















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
});


}
});














draft saved

draft discarded


















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
















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Feedback on college project

Futebolista

Albești (Vaslui)