我有一个场景,需要将一个对象编组为XML,该对象可以是多种类型,因此我创建了一个接口,将该对象类型设置为泛型T。
interface FileMarshaller<T> {
fun marshal(object: T): File
}
我将有这个接口的几个实现,每种类型的对象一个,这是一个:
internal class DocumentFileMarshaller : FileMarshaller<Document> {
override fun marshal(document: Document): File {
//some kotlin code
}
}
这很好,做了我需要的。问题出在工厂里。这个工厂接收一个参数,并根据那个参数决定它应该检索哪个编组器。
class FileMarshallerFactory {
internal fun createMarshaller(fileType: FileTypes): FileMarshaller<What should I put here?> {
when (fileType) {
FileTypes.DOCUMENT -> {
return DocumentFileMarshaller()
}
else -> throw MarshallerNotFound(MARSHALLER_NOT_FOUND)
}
}
}
问题是工厂的返回类型,不能空,不能把Document作为它的类型,因为会有更多的实现,这个类型参数怎么处理,也许除了我创建的这个工厂之外还有别的方法?
2条答案
按热度按时间laik7k3q1#
如果还在函数参数中包含泛型类型信息,则可以使函数的返回类型为泛型。
例如,这个函数要求向它传递类型信息,以便它可以确定返回什么类型。
函数名
listOf
后面的<String>
是泛型类型信息,函数需要该信息才能知道它应该返回List<String>
您可以像这样编写类,以便在函数调用中包含类型信息。
要调用这个新函数,必须包含如下类型信息:
但在我看来这有点多余。
更好的方式
或者,也可以使用给定的类型信息来确定要示例化哪个FileMarshaller。
我们这里有一个带具体化类型参数的内联函数。类型信息通常只在编译时可用,而在运行时由于 type erasure 的原因而不可用。将函数与具体化类型内联可以使类型信息在运行时可用,这样你就可以在
when
中使用它来决定示例化哪个FileMarshaller。有关Kotlin类型擦除的更多信息,请单击此处:https://kotlinlang.org/docs/generics.html#type-erasure
为什么不是FileMarshaller?
有些人可能会说解决你的问题最简单的方法是返回一个
FileMarshaller<Any>
,这将是有效的和可编译的,但我不同意这种方法,虽然这可能会工作,它也会牺牲类型安全,因为现在你可以调用marshall(Any)
,并给予它任何参数。hi3rlvi22#
在您的例子中,
FileTypes
决定了结果类型,并且它总是与特定的类型相关,因此参数化它也是有意义的,然后它被用作T
的“源”:注意,这种方法不能使用枚举,需要一个密封的接口/类。
而且,老实说,这种代码设计让我觉得有点奇怪,如果有有限数量的封送程序,调用者需要知道它到底获取了哪个封送程序,那么它必须知道
Document
和FileTypes.DOCUMENT
,那么为每个封送处理程序建立单独的工厂不是更容易吗?可以通过多个函数,也可以通过完全独立的类。也许这是因为我们没有我看不到全貌,只看到一个简化的例子。