首页 > 解决方案 > 如何在 Kotlin 中为泛型类型创建 DI 绑定?

问题描述

我正在尝试找到一个基于 Kotlin 的依赖注入系统,该系统将允许我根据需要绑定诸如Foo<T>和 resolve Foo<Int>Foo<String>等泛型类型。

Kodein 似乎是更流行的 DI 框架之一,我已经研究过,但不知道它是如何可能的。Kodein 存储库中的Issue #83围绕该主题进行了一些讨论。

就我而言,我希望能够做这样的事情:

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

在 C# 中,使用 Autofac 非常简单。下面的代码片段是我想要的 C# 中的一个工作实现:

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

输出

Add Node (add integers - Experiment.IntNumInstance)
Add Node (add doubles - Experiment.DoubleNumInstance)

标签: genericsdependency-injectionkotlin

解决方案


推荐阅读