kotlin数据类_Kotlin数据类中的潜在陷阱

kotlin数据类

这篇文章的目的不是指出Kotlin数据类设计中的一些重大缺陷,而是向您展示如何通过它们。 实际上,情况恰恰相反。 Kotlin文档中清楚地记录了这篇文章的内容。 我只是在这里向没有确切注意到其数据类如何工作的任何人突出此信息。

数据类对我们的开发人员来说非常方便,特别是那些来自Java的开发人员。 它们提供了几个生成的函数,使您可以用很少的代码编写功能齐全的类。 数据类提供以下生成的函数:

以上所有都是针对类的主构造函数中定义的属性生成的。 在此构造函数之外定义的任何内容都将被忽略。 这就是潜在的陷阱 。 但是,这只是一个陷阱,如果您不知道数据类如何工作。 正如我之前提到的,它已明确记录在案 ,您只需要注意这一点。 当然,您现在是。

如果您对定义数据类的方式不谨慎,则可能会在应用程序中发现一些错误。 equalshashCode通常是必不可少的函数。 如果它们不能按预期工作,则肯定会出现错误。

下面是一个示例:

 data class MyClass(val a: String, val b: Int) { 
   // property defined outside of primary constructor 
   lateinit var c: String  }  fun main() { 
   // create two equal objects 
   val myClass = MyClass( "abc" , 0 ) 
   val myClass2 = myClass.copy() 
   // check their hashCodes are the same and that they equal each other 
   println( "myClass hashCode: ${myClass.hashCode()}" ) 
   println( "myClass2 hashCode: ${myClass2.hashCode()}" ) 
   println( "myClass == myClass2: ${myClass == myClass2}" ) 
   // set the lateinit variables 
   myClass.c = "im a lateinit var" 
   myClass2.c = "i have a different value" 
   // have their hashCodes changed? 
   println( "myClass hashCode after setting lateinit var: ${myClass.hashCode()}" ) 
   println( "myClass2 hashCode after setting lateinit var: ${myClass2.hashCode()}" ) 
   // are they still equal to each other? 
   println( "myClass == myClass2 after setting lateinit vars: ${myClass == myClass2}" ) 
   // sanity check to make sure I'm not being stupid 
   println( "sanity checking myClass.c: ${myClass.c}" ) 
   println( "sanity checking myClass2.c: ${myClass2.c}" )  } 

执行此示例输出:

 myClass hashCode: 2986974 myClass hashCode: 2986974  myClass2 hashCode: 2986974  myClass == myClass2: true  myClass hashCode after setting lateinit var: 2986974  myClass2 hashCode after setting lateinit var: 2986974  myClass == myClass2 after setting lateinit vars: true  sanity checking myClass.c: im a lateinit var  sanity checking myClass2.c: i have a different value 

正如您所看到的,即使每个对象的c属性不同,它们的hashCode也是相同的,并且彼此相等。 如果您试图在MapSet使用MyClass ,则条目相互碰撞的机会会增加。 话虽如此,这确实取决于您要实现的目标。 也许这正是您想要发生的事情。 在这种情况下,您将获得更多动力。

c放入MyClass构造函数中会影响hashCodeequals实现。 然后, c将涉及对hashCodeequals和其余生成函数的任何调用。

另一个解决方案是手动实现生成的功能。 将类重写为:

 data class MyClass(val a: String, val b: Int) { 
   lateinit var c: String 
   override fun equals(other: Any?): Boolean { 
     if ( this === other) return true 
     if (javaClass != other?.javaClass) return false 
     other as MyClass 
     if (a != other.a) return false 
     if (b != other.b) return false 
     if (c != other.c) return false 
     return true 
   } 
   override fun hashCode(): Int { 
     var result = a.hashCode() 
     result = 31 * result + b 
     result = 31 * result + c.hashCode() 
     return result 
   } 
   override fun toString(): String { 
     return "MyClass(a='$a', b=$b, c='$c')" 
   }  } 

这些实现由Intellij ly提供。 通过在每个重写函数中指定所有属性,现在可以使用主构造函数(本例中为c )中未包含的所有属性。 hashCodeequals现在可以更好地表示该类,并改进其在MapSet

但是,又大又😏。 至少在我编写的代码中。 现在引入了一个错误。 c是一个lateinit var ,每个覆盖的函数现在都尝试访问它。 如果在设置c之前调用了这些函数中的任何一个,您将得到一个异常:

 Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property c has not been initialized 
     at dev.lankydan.MyClass.hashCode(DataClasses.kt: 60 ) 
     at dev.lankydan.DataClassesKt.main(DataClasses.kt: 72 ) 
     at dev.lankydan.DataClassesKt.main(DataClasses.kt) 

重写equalshashCodetoString以容纳lateinit var将解决此错误:

 data class MyClass(val a: String, val b: Int) { 
   lateinit var c: String 
   override fun equals(other: Any?): Boolean { 
     if ( this === other) return true 
     if (javaClass != other?.javaClass) return false 
     other as MyClass 
     if (a != other.a) return false 
     if (b != other.b) return false 
     if ( this ::c.isInitialized && (other as MyClass)::c.isInitialized && c != other.c) return false 
     return true 
   } 
   override fun hashCode(): Int { 
     var result = a.hashCode() 
     result = 31 * result + b 
     if ( this ::c.isInitialized) { 
       result = 31 * result + c.hashCode() 
     } 
     return result 
   } 
   override fun toString(): String { 
     return if ( this ::c.isInitialized) "MyClass(a='$a', b=$b, c='$c')" 
     else "MyClass(a='$a', b=$b)" 
   }  } 

即使未设置lateinit var ,该实现也可以安全使用。

是否要这样做取决于班级的要求。 不建议使用像我在Map这样的数据类。 如果您想这样做,可以。 只是要知道它是如何工作的。

如果您还没有这样做,建议您快速浏览有关此主题的文档 。 突出此信息是本文的目标。 并不是一些花哨的代码可以做一些神奇的事情。 取而代之的是,这对于Kotlin的工作方式来说是更为基本和根本的。 了解数据类在这方面的工作方式对于减少应用程序中的错误数量至关重要。

翻译自: https://www.javacodegeeks.com/2019/08/potential-traps-kotlins-data-classes.html

kotlin数据类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值