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_Reflection_Annotations_Data Class - Fatal编程技术网

Kotlin数据类上的属性包括/排除

Kotlin数据类上的属性包括/排除,kotlin,reflection,annotations,data-class,Kotlin,Reflection,Annotations,Data Class,假设我只希望在生成的equals和hashCode实现中包含一个或两个字段(或者排除一个或多个字段)。对于简单类,例如: data class Person(val id: String, val name: String) Groovy有以下特点: @EqualsAndHashCode(includes = 'id') @EqualsAndHashCode(of = "id") 龙目岛有: @EqualsAndHashCode(includes = 'id') @E

假设我只希望在生成的equals和hashCode实现中包含一个或两个字段(或者排除一个或多个字段)。对于简单类,例如:

data class Person(val id: String, val name: String)
Groovy有以下特点:

@EqualsAndHashCode(includes = 'id')
@EqualsAndHashCode(of = "id")
龙目岛有:

@EqualsAndHashCode(includes = 'id')
@EqualsAndHashCode(of = "id")
Kotlin的惯用做法是什么

我目前的做法
只是感觉不对。。。我真的不希望
name
是可变的,而且额外的构造函数定义很难看。

这里有一个有点创造性的方法:

data class IncludedArgs(val args: Array<out Any>)

fun includedArgs(vararg args: Any) = IncludedArgs(args)


abstract class Base {
    abstract val included : IncludedArgs

    override fun equals(other: Any?) = when {
        this identityEquals other -> true
        other is Base -> included == other.included
        else -> false
    }

    override fun hashCode() = included.hashCode()

    override fun toString() = included.toString()
}

class Foo(val a: String, val b : String) : Base() {
    override val included = includedArgs(a)
}

fun main(args : Array<String>) {
    val foo1 = Foo("a", "b")
    val foo2 = Foo("a", "B")

    println(foo1 == foo2) //prints "true"
    println(foo1)         //prints "IncludedArgs(args=[a])"
}
包含参数的数据类(val-args:Array)
fun includedArgs(vararg-args:Any)=includedArgs(args)
抽象类基{
包含的抽象值:IncludedArgs
覆盖乐趣等于(其他:任何?)=何时{
此标识等同于其他->真
其他为基础->包含==其他。包含
else->false
}
重写fun hashCode()=包含的.hashCode()
重写fun-toString()=包含的.toString()
}
类Foo(val a:String,val b:String):Base(){
覆盖val included=includedArgs(a)
}
趣味主线(args:Array){
val foo1=Foo(“a”、“b”)
val foo2=Foo(“a”、“B”)
println(foo1==foo2)//打印“true”
println(foo1)//打印“IncludedArgs(args=[a])”
}
我也不知道Kotlin(1.1)中的“idomatic方式”如何做到这一点

我最终覆盖了
equals
hashCode

data class Person(val id: String,
                  val name: String) {

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other?.javaClass != javaClass) return false

        other as Person

        if (id != other.id) return false

        return true
    }

    override fun hashCode(): Int {
        return id.hashCode()
    }
}

难道没有“更好”的方法吗?

我用过这种方法

data class Person(val id: String, val name: String) {
   override fun equals(other: Person) = EssentialData(this) == EssentialData(other)
   override fun hashCode() = EssentialData(this).hashCode()
   override fun toString() = EssentialData(this).toString().replaceFirst("EssentialData", "Person")
}

private data class EssentialData(val id: String) {
   constructor(person: Person) : this(id = person.id) 
}

这基于@bashor的方法,并使用私有主构造函数和公共辅助构造函数。遗憾的是,equals要忽略的属性不能是val,但可以隐藏setter,因此从外部角度来看,结果是等效的

data class ExampleDataClass private constructor(val important: String) {
  var notSoImportant: String = ""
    private set

  constructor(important: String, notSoImportant: String) : this(important) {
    this.notSoImportant = notSoImportant
  }
}

可重用解决方案:为了有一个简单的方法来选择在
equals()
hashCode()
中包含哪些字段,我编写了一个名为“stem”(与相等相关的基本核心数据)的小助手

使用非常简单,生成的代码非常小:

class Person(val id: String, val name: String) {
    private val stem = Stem(this, { id })

    override fun equals(other: Any?) = stem.eq(other)
    override fun hashCode() = stem.hc()
}
可以通过动态的额外计算来权衡类中存储的支持字段:

    private val stem get() = Stem(this, { id })
由于
Stem
接受任何函数,因此您可以自由指定等式的计算方式。对于要考虑的多个字段,只要添加每个字段的一个lambda表达式(VARARGS):


实施:

class Stem<T : Any>(
        private val thisObj: T,
        private vararg val properties: T.() -> Any?
) {     
    fun eq(other: Any?): Boolean {
        if (thisObj === other)
            return true

        if (thisObj.javaClass != other?.javaClass)
            return false

        // cast is safe, because this is T and other's class was checked for equality with T
        @Suppress("UNCHECKED_CAST") 
        other as T

        return properties.all { thisObj.it() == other.it() }
    }

    fun hc(): Int {
        // Fast implementation without collection copies, based on java.util.Arrays.hashCode()
        var result = 1

        for (element in properties) {
            val value = thisObj.element()
            result = 31 * result + (value?.hashCode() ?: 0)
        }

        return result
    }

    @Deprecated("Not accessible; use eq()", ReplaceWith("this.eq(other)"), DeprecationLevel.ERROR)
    override fun equals(other: Any?): Boolean = 
        throw UnsupportedOperationException("Stem.equals() not supported; call eq() instead")

    @Deprecated("Not accessible; use hc()", ReplaceWith("this.hc(other)"), DeprecationLevel.ERROR)
    override fun hashCode(): Int = 
        throw UnsupportedOperationException("Stem.hashCode() not supported; call hc() instead")
}
如果这些方法是隐式调用或通过反射调用的,那么异常只是一种回退;如果有必要,可以进行辩论


当然,
Stem
类可以进一步扩展,以包括自动生成
toString()
等。

此方法可能适用于属性排除:

class SkipProperty<T>(val property: T) {
  override fun equals(other: Any?) = true
  override fun hashCode() = 0
}
您可以通过定义enum,创建一个将属性排除表示为
@ExcludeToString
或带有
@ToString(Type.EXCLUDE)
参数的

然后使用格式设置
getToString()
的值

@Target(AnnotationTarget.FIELD)
@保留(AnnotationRetention.RUNTIME)
注释类ExcludeToString
数据类测试(
var a:String=“测试a”,
@ExcludeToString变量b:String=“测试b”
) {
重写funtostring():String{
返回ExcludeToString.getToString(此)
}
}
对象排除字符串{
有趣的getToString(对象:任意):字符串{
val-toString=LinkedList()
getFieldsNotExludeToString(obj).forEach{prop->
prop.isAccessible=true
toString+=“${prop.name}=“+prop.get(obj)?.toString()?.trim()
}
返回“${obj.javaClass.simpleName}=[${toString.joinToString(“,”)}”
}
私人娱乐getFieldsNotExludeToString(对象:任意):列表{
val declaredFields=obj::class.java.declaredFields
返回declaredFields.filterNot{field->
isFieldWithExludeToString(字段)
}
}
private fun isFieldWithExludeToString(字段:字段):布尔值{
field.annotations.forEach{
if(it.annotationClass==ExcludeToString::class){
返回真值
}
}
返回错误
}
}
德国劳埃德船级社


更简单、更快,请查看此处或Kotlin文档。
只有主构造函数中的字段才会被考虑来构建自动访问方法,比如equals等等。把那些没有意义的东西放在外面。

有趣的解决办法!我个人不会交换几行样板赋值,例如@bashor示例中的
val name:String=name
,用于从
Base
类继承,以弥补缺少的语言特性。我同意,我的解决方案不是很优雅。我只是为了好玩而把它拼凑在一起,并决定与大家分享。我认为我的解决方案也不完美,但有一些好处。看看。equals应该覆盖“Any?”这看起来有点麻烦。这仍然是最新的最佳解决方案吗?
equals
fun的改进:
override-fun-equals(other:Any?):Boolean{if(other!=Person)return false-return-EssentialData(this)==EssentialData(other)}
编写@dstibbe改进的更惯用方法是:
override-fun-equals(other:Any?)=other is Person&&EssentialData(this)==EssentialData(other)
Nice improvement@KristopherNoronha。这很有创意,因此+1是一种解决方案,但这不是我会采用的解决方案。对字段的任何访问都有额外的
.property
,该字段缺少对其包含类的equals/hashCode的参与,这只是该类的一个实现细节。当然,您可以在该属性上重写
get()/set()
,以自动执行此操作,但是ooof。对这样的要求来说很重。是的,你是对的。我只想分享我的尝试。很有趣,谢谢。我会试试的!
class SkipProperty<T>(val property: T) {
  override fun equals(other: Any?) = true
  override fun hashCode() = 0
}
data class Person(
    val id: String, 
    val name: SkipProperty<String>
)
@Target(AnnotationTarget.FIELD)
@Retention(AnnotationRetention.RUNTIME)
annotation class ExcludeToString

data class Test(
        var a: String = "Test A",
        @ExcludeToString var b: String = "Test B"
) {
    override fun toString(): String {
        return ExcludeToStringUtils.getToString(this)
    }
}

object ExcludeToStringUtils {

    fun getToString(obj: Any): String {
        val toString = LinkedList<String>()
        getFieldsNotExludeToString(obj).forEach { prop ->
            prop.isAccessible = true
            toString += "${prop.name}=" + prop.get(obj)?.toString()?.trim()
        }
        return "${obj.javaClass.simpleName}=[${toString.joinToString(", ")}]"
    }

    private fun getFieldsNotExludeToString(obj: Any): List<Field> {
        val declaredFields = obj::class.java.declaredFields
        return declaredFields.filterNot { field ->
            isFieldWithExludeToString(field)
        }
    }

    private fun isFieldWithExludeToString(field: Field): Boolean {
        field.annotations.forEach {
            if (it.annotationClass == ExcludeToString::class) {
                return true
            }
        }
        return false
    }

}