联盟类型/扩展接口

我有几个data class与字段,这是在形式中使用,并需要他们有一个方法返回true如果任何字段已被填写。

我不想为所有的课程重写这个,所以我现在这样做:

 data class Order(var consumer: String, var pdfs: List<URI>): Form { override val isEmpty(): Boolean get() = checkEmpty(consumer, pdfs) } data class SomethingElse(var str: String, var set: Set<String>): Form { override val isEmpty(): Boolean get() = checkEmpty(str, set) } interface Form { val isEmpty: Boolean fun <T> checkEmpty(vararg fields: T): Boolean { for (f in fields) { when (f) { is Collection<*> -> if (!f.isEmpty()) return false is CharSequence -> if (!f.isBlank()) return false } } return true; } } 

这显然不是很漂亮,也不是类型安全的。

什么是这样做的更习惯性的方式,而不是抽象每一个属性到某种Field类型?

说明:我正在寻找的是一种方法,例如通过提供所有允许的类型( StringIntListSet )和每个函数来判断它们是否为空, 像一个方法isEmptyFormField的“扩展接口”。

如果你正在做的是检查isEmpty / isBlank / isZero /等。 那么你可能不需要一个通用的checkEmpty函数等:

 data class Order(var consumer: String, var pdfs: List<URI>) : Form { override val isEmpty: Boolean get() = consumer.isEmpty() && pdfs.isEmpty() } data class SomethingElse(var str: String, var set: Set<String>) : Form { override val isEmpty: Boolean get() = str.isEmpty() && set.isEmpty() } interface Form { val isEmpty: Boolean } 

然而,如果你实际上做了一些更复杂的事情,那么根据你的补充说明,我相信“将每个属性抽象成某种类型的Field ”正是你所希望的,而不是让Field实例成为每个data class一部分data class ,而是在需要时创建它们的列表:

 data class Order(var consumer: String, var pdfs: List<URI>) : Form { override val fields: List<Field<*>> get() = listOf(consumer.toField(), pdfs.toField()) } data class SomethingElse(var str: String, var set: Set<String>) : Form { override val fields: List<Field<*>> get() = listOf(str.toField(), set.toField()) } interface Form { val isEmpty: Boolean get() = fields.all(Field<*>::isEmpty) val fields: List<Field<*>> } fun String.toField(): Field<String> = StringField(this) fun <C : Collection<*>> C.toField(): Field<C> = CollectionField(this) interface Field<out T> { val value: T val isEmpty: Boolean } data class StringField(override val value: String) : Field<String> { override val isEmpty: Boolean get() = value.isEmpty() } data class CollectionField<out C : Collection<*>>(override val value: C) : Field<C> { override val isEmpty: Boolean get() = value.isEmpty() } 

这样可以在不改变data class组件等的情况下为您提供类型安全性,并允许您在“无所不能when ”。

这有点哈克,但应该工作。 每个data class为每个构造函数参数创建一组方法。 它们被称为componentN() (其中N是从1开始的编号,指示构造函数参数)。

你可以把这样的方法放在你的接口中,并使data class隐式地实现它们。 看下面的例子:

 data class Order(var consumer: String, var pdfs: List) : Form data class SomethingElse(var str: String, var set: Set) : Form interface Form { val isEmpty: Boolean get() = checkEmpty(component1(), component2()) fun checkEmpty(vararg fields: T): Boolean { for (f in fields) { when (f) { is Collection -> if (!f.isEmpty()) return false is CharSequence -> if (!f.isBlank()) return false } } return true; } fun component1(): Any? = null fun component2(): Any? = null } 

你也可以添加fun component3(): Any? = null fun component3(): Any? = null等…来处理data class多于2个字段的情况(例如, NullObject模式或直接在您的checkEmpty()方法中处理null

正如我所说,这有点哈克,但也许会为你工作。

您可以使用null来表示“未指定”:

 data class Order(var consumer: String?, var pdfs: List<URI>?) : Form { override val isEmpty: Boolean get() = checkEmpty(consumer, pdfs) } data class SomethingElse(var str: String?, var set: Set<String>?) : Form { override val isEmpty: Boolean get() = checkEmpty(str, set) } interface Form { val isEmpty: Boolean fun <T> checkEmpty(vararg fields: T): Boolean = fields.all { field -> field == null } } 

这里的想法和Java中的Optional<T>是一样的,但没有额外的对象等等。

你现在不得不担心空的安全性,但如果你的领域是有一个缺席/空的概念,那么这似乎是适当的( UsingAndAvoidingNullExplained·谷歌/番石榴维基 )。