【问题标题】:Kotlin Factory Class with Generic outputs具有通用输出的 Kotlin 工厂类
【发布时间】:2021-04-14 07:03:04
【问题描述】:

我正在尝试将我用 Java 编写的一些代码移植到 Kotlin 中,并且我正在努力解决有关泛型的一些问题。我通常在 Java 中使用工厂模式来返回我想要为给定类型调用的通用接口的实例。

在 Java 中我有这个合同:

public Message<T extends Action> {
    private List<T> actions;

    ..some other properties
}

还有这个界面:

public interface MessageConverter<T extends Action, M extends BaseModel> {
    List<M> convertMessage(Message<T> message);

    DataType getDataType();
}

最后是这个工厂:

public class MessageConverterFactory {
    //This gets populated via DI
    private Map<DataType, MessageConverter> converterMap;

    public <T extends Action, M extends BaseModel> MessageConverter<T, M> getMessageConverter(DataType dataType) {
        return converterMap.get(dataType);
    }
}

有了这一切,我就可以做这样的事情了:

Message<T> message = mapper.readValue(messageString, type);

MessageConverter<T, M> messageConverter = messageConverterFactory.getMessageConverter(dataType);

List<M> dataModels = messageConverter.convertMessage(message);

我知道我在一定程度上滥用 Java 中的原始泛型类型来实现这一点,但我认为仍有一些方法可以实现这样的泛型工厂模式。

但是,无论我尝试使用通用方差、星形投影等,我都无法让 Kotlin 接受此代码的任何版本。我得到的最接近的是调用通用转换器的 convertMessage 调用。它失败了,因为我使用星形投影并试图限制 T 的类型,但这导致编译器认为 convertMessage 接受 Message

这样的代码在 Kotlin 中是否可行?或者我应该使用类似的替代方法吗?

谢谢, 杰夫

【问题讨论】:

  • 如果您使用星形投影并显式转换为您要返回的类型,您仍然可以在 Kotlin 中执行相当于滥用原始类型的操作。

标签: kotlin generics


【解决方案1】:

this 到 Kotlin 的字面转换非常简单,IDEA 内置的 Java-to-Kotlin 转换器几乎可以直接输出类似这样的内容,给定等效的 Java 代码:

class Message<T: Action> {
  private val actions: List<T> = TODO()
  ...
}

interface MessageConverter<T: Action, out M: BaseModel> {
  fun convertMessage(message: Message<T>): List<M>

  val dataType: DataType
}

class MessageConverterFactory(val converterMap: Map<DataType, MessageConverter<*, *>>) {
  fun <T: Action, M: BaseModel> getMessageConverter(dataType: DataType): MessageConverter<T, M> {
    return converterMap[dataType] as MessageConverter<T, M>
  }
}

注意,getMessageConverter 中的强制转换——你的 Java 代码在做同样的事情,但没有明确说明——我相信编译器甚至会发出关于未检查分配的警告。

Kotlin 中的另一种方法是使用具有具体类型的内联函数来返回适当的转换器。例如,像这样:

inline fun <reified T: Action, reified M: BaseModel> converterOf(): MessageConverter<T, M> = when {
  T::class == FooAction::class, M::class == BarModel::class -> TODO()
  else -> error("No converter available for type ${T::class.simpleName} to ${M::class.simpleName}")
}

【讨论】:

  • 就是这样。对于它的价值,是否有一种“正确”的方式来做这种不涉及未经检查的演员的事情?
  • @Greenbones 我认为这取决于您如何为转换器进行 DI。例如,也许您可​​以使用某种子类型绑定来绑定MessageConverters,例如来自 Kodein:docs.kodein.org/kodein-di/7.5/core/….
  • 或者不要让它成为“未经检查的”强制转换,您可以在强制转换之前手动进行类型检查。您可能会内联 getMessageConverter 并具体化 TM,并使用该信息进行检查。
  • 我现在在工厂中实际获得的代码只是自动装配我明确需要的每种类型并打开传入的 dataType。但即便如此,它也不喜欢在没有未经检查的强制转换的情况下将这些实现作为 MessageConverter 返回(即使特定实现满足接口上的约束)。
  • @Greenbones 我在答案中添加了一个选项,该选项使用 Kotlin 的具体类型返回正确的转换器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-21
  • 1970-01-01
相关资源
最近更新 更多