联盟类型/扩展接口
我有几个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
类型?
说明:我正在寻找的是一种方法,例如通过提供所有允许的类型( String
, Int
, List
, Set
)和每个函数来判断它们是否为空, 像一个方法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·谷歌/番石榴维基 )。