Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 以多态方式处理许多类似的单态类_Kotlin - Fatal编程技术网

Kotlin 以多态方式处理许多类似的单态类

Kotlin 以多态方式处理许多类似的单态类,kotlin,Kotlin,我有一个库,它生成了一大堆外观非常相似的Java类。为了简洁起见,让我们在Kotlin中指定类似的内容: data class A(val commonParam1: String, val commonParam2: String, val specialParam: Int) data class B(val commonParam1: String, val commonParam2: String, val specialParam: String) data class C(val c

我有一个库,它生成了一大堆外观非常相似的Java类。为了简洁起见,让我们在Kotlin中指定类似的内容:

data class A(val commonParam1: String, val commonParam2: String, val specialParam: Int)
data class B(val commonParam1: String, val commonParam2: String, val specialParam: String)
data class C(val commonParam1: String, val commonParam2: String, val specialParam: Boolean)
data class D(val commonParam1: String, val commonParam2: String, val specialParam: Float)
显然,最好生成一个通用类或接口:

data class G<T>(val commonParam1: String, val commonParam2: String, val specialParam: T)
数据类G(val commonParam1:String,val commonParam2:String,val specialParam:T)
但是库不会生成这种代码。我有几十个这样的类,它们在不同的地方生成,所以这是一个m×n的情况。不幸的是,这些类没有共享一个公共接口(我可以使用)。因此,在我看来,有五个选项可以解决这个问题:

  • 编写与任何生成的类一起工作的所有代码m×n次。这是一大堆复制品,但它一点也不复杂,而且是安全的
  • 使用反射。首先,生成这些类背后的想法是增加类型安全性。反思会再次把它扔出窗外,所以它会让人觉得适得其反
  • 对我自己的代码使用代码生成。这感觉像是一种有点笨手笨脚的方法,我的团队没有代码生成的经验
  • 使用。那太好了,但我可能会接受目前存在的黑客攻击。我仍然需要提供大量的boiler plate,因为我必须为这些类中的每一个提供接口证据
  • 使用准引号,但我认为我还不应该在生产中使用Arrow Meta
我还有其他选择吗?有没有一种方法可以对类似的类进行抽象


在这个具体的例子中,我的Graphql模式有很多突变,它们采用了非常相似的
mutationput
。我们不直接控制模式的形状。由于每个突变也有其自己的输入和输出类型,因此构建这些突变并解释其输出是一项繁重的工作,即使我大量使用片段,也会产生大量重复的代码,因为即使提取片段的代码也必须重复


这在Graphql中通常不是问题,因为大部分都是在JS或TS中完成的,两者都有duck类型。Kotlin没有duck类型。

我在这里列出了多个选项,其中一些选项可以组合使用以重用代码并生成最少的繁琐代码

与默认实现的接口 您可以使用具有默认函数实现的接口来提供公共基础:

interface CommonDataInterface<T> {
    val commonParam1: String
    val commonParam2: String
    val specialParam: T

    fun foo() { ...implementation here }
    fun bar() { ...implementation here }
    fun zoo(x: T): T { ...implementation here }
}

// concrete variations, using common interface functions implemented above:

data class A(override val commonParam1: String, 
             override val commonParam2: String, 
             override val specialParam: Int) 
             : CommonDataInterface<Int>
data class B(override val commonParam1: String, 
             override val commonParam2: String, 
             override val specialParam: String) 
             : CommonDataInterface<String>
data class C(override val commonParam1: String, 
             override val commonParam2: String, 
             override val specialParam: Boolean) 
             : CommonDataInterface<Boolean>
data class D(override val commonParam1: String, 
             override val commonParam2: String, 
             override val specialParam: Float) 
             : CommonDataInterface<Float>
但是上面提到的不允许任何公共函数使用
specialParam
,它将按照专门化来实现


使用注释处理器生成类的置换 您可以编写自己的注释处理器,这是代码生成或其他编译时操作的一种形式,向特殊类添加注释,并让注释处理器生成字节码中的所有排列。然后,它可以被烘焙到您的编译过程中,神奇地,这些额外的类就会出现

您需要了解Kotlin是如何进行注释处理的,以及该主题在某些地方的一般情况。此外,post Kotlin 1.4可能是一个编译器插件API,可以帮助编写完成类似工作的插件

有关注释处理的一些链接表明,使用注释处理并不是那么困难:


使用Kotlin的代码生成库生成Kotlin代码
查看Kotlin代码生成的一个简单方法是库。这是一种程序化的代码生成方法,即使对于没有代码生成经验的团队来说也很容易。这可以与前面提到的关于如何构造类的变体一起使用。它还可以与注释处理一起使用,作为编译过程中触发代码生成的方法。

这些类只是空数据容器吗?没有方法或逻辑?它们是否需要是数据类(因为您对数据类继承有限制)?这些问题很重要,因为它们会影响可能的解决方案。它们确实有一些逻辑/方法,但在大多数情况下,它们是数据传输对象。好的,我在回答中给出了一些不同的解决方案,以及从注释处理器生成代码的选项。我不控制基本数据类,因此无法添加接口。但是我认为我可能能够使用代码生成来复制基类,并让它们实现公共接口。也许,我得去看看。
class G<T>(val commonParam1: String, val commonParam2: String, val specialParam: T) { 
    fun foo() { ...implementation here }
    fun bar() { ...implementation here }
    fun zoo(x: T): T { ...implementation here }
}

// concrete variations, using common functions from base class

class A(commonParam1: String, commonParam2: String, specialParam: Int) 
         : G<Int>(commonParam1, commonParam2, specialParam)
class B(commonParam1: String, commonParam2: String, specialParam: String)
         : G<String>(commonParam1, commonParam2, specialParam)
class C(commonParam1: String, commonParam2: String, specialParam: Boolean)
         : G<Boolean>(commonParam1, commonParam2, specialParam)
class D(commonParam1: String, commonParam2: String, specialParam: Float)
         : G<Float>(commonParam1, commonParam2)
interface CommonDataInterface {
    val commonParam1: String
    val commonParam2: String

    fun foo() 
    fun bar() 
}

data class CommonData(override val commonParam1: String, 
                      override val commonParam2: String)
                      : CommonDataInterface {
    override fun foo() { ...implementation here  }
    override fun bar() { ...implementation here  }
}

// concrete implementations using common functions via automatic delegation 

data class A(private val common: CommonDataInterface, 
             val specialParam: Int) : CommonDataInterface by common {
    fun zoo(x: Int): Int { ...implementation here using specialParam }
}

data class B(private val common: CommonDataInterface, 
             val specialParam: String) : CommonDataInterface by common {
    fun other(y: String): String { ...implementation here using specialParam }
}

data class C(private val common: CommonDataInterface, 
             val specialParam: Boolean) : CommonDataInterface by common

data class D(private val common: CommonDataInterface, 
             val specialParam: Float) : CommonDataInterface by common