Kotlin - 为非数据生成 toString() class
Kotlin - generate toString() for a non-data class
情况:
我有一个带有 lateinit
字段的 class,因此它们不存在于构造函数中:
class ConfirmRequest() {
lateinit var playerId: String
}
我想要一个包含所有字段的 toString()
方法,不想手动编写它,以避免重复打印。在 Java 中,我将使用 Lombok @ToString
注释来解决这个问题。
问题:
有什么方法可以在 Kotlin 中实现吗?
推荐的方式是手动写toString
(或者IDE生成)希望大家不要有太多这样的类.
data class
的目的是为了适应 85% 的最常见情况,剩下 15% 给其他解决方案。
像你一样,我习惯在 toString()
和 Java 中使用 lombok equals()
,所以有点失望的是 Kotlin 中需要非数据 类所有标准样板文件。
所以我创建了 Kassava,一个开源库,让您无需任何样板即可实现 toString()
和 equals()
- 只需提供属性列表即可!
例如
// 1. Import extension functions
import au.com.console.kassava.kotlinEquals
import au.com.console.kassava.kotlinToString
import java.util.Objects
class Employee(val name: String, val age: Int? = null) {
// 2. Optionally define your properties for equals()/toString() in a companion
// object (Kotlin will generate less KProperty classes, and you won't have
// array creation for every method call)
companion object {
private val properties = arrayOf(Employee::name, Employee::age)
}
// 3. Implement equals() by supplying the list of properties to be included
override fun equals(other: Any?) = kotlinEquals(
other = other,
properties = properties
)
// 4. Implement toString() by supplying the list of properties to be included
override fun toString() = kotlinToString(properties = properties)
// 5. Implement hashCode() because you're awesome and know what you're doing ;)
override fun hashCode() = Objects.hash(name, age)
}
我发现 Apache Commons Lang 的 ToStringBuilder
with reflection 很有用,但它会在我不需要时调用 hashCode()
和其他方法(还有一个调用 hashCode()
来自 3rd- party lib 生成一个 NPE)。
所以我选择了:
// class myClass
override fun toString() = MiscUtils.reflectionToString(this)
// class MiscUTils
fun reflectionToString(obj: Any): String {
val s = LinkedList<String>()
var clazz: Class<in Any>? = obj.javaClass
while (clazz != null) {
for (prop in clazz.declaredFields.filterNot { Modifier.isStatic(it.modifiers) }) {
prop.isAccessible = true
s += "${prop.name}=" + prop.get(obj)?.toString()?.trim()
}
clazz = clazz.superclass
}
return "${obj.javaClass.simpleName}=[${s.joinToString(", ")}]"
}
您可以定义一个数据 class,其中包含您要使用的数据,并通过委托给它来实现方法。
使用 Kotlin 反射怎么样?我进入 Kotlin 几天了,很抱歉,如果我误解了问题,或者写了 "Kotlin inefficient" 示例。
override fun toString() : String{
var ret : String = ""
for (memberProperty in this.javaClass.kotlin.memberProperties){
ret += ("Property:${memberProperty.name} value:${memberProperty.get(this).toString()}\n");
}
return ret
}
这也可以在新创建的接口中实现,例如 ToString2Interface as fun toString2。那么所有实现 ToString2Interface 的 类 都将具有 toString2()
这就是我最终要做的。
在 Any
class
上创建扩展函数
fun Any.toStringByReflection(exclude: List<String> = listOf(), mask: List<String> = listOf()): String {
val propsString = this::class.memberProperties
.filter { exclude.isEmpty() || !exclude.contains(it.name) }
.joinToString(", ") {
val value = if (!mask.isEmpty() && mask.contains(it.name)) "****" else it.getter.call(this).toString()
"${it.name}=${value}"
};
return "${this::class.simpleName} [${propsString}]"
}
然后你可以从单个类型调用这个方法。
override fun toString(): String {
return this.toStringByReflection()
}
生成下面的字符串
Table [colums=[], name=pg_aggregate_fnoid_index, schema=pg_catalog, type=SYSTEM INDEX]
名称字段被屏蔽:
override fun toString(): String {
return this.toStringByReflection(mask= listOf("name"))
}
它生成,
Table [colums=[], name=****, schema=pg_catalog, type=SYSTEM INDEX]
情况:
我有一个带有 lateinit
字段的 class,因此它们不存在于构造函数中:
class ConfirmRequest() {
lateinit var playerId: String
}
我想要一个包含所有字段的 toString()
方法,不想手动编写它,以避免重复打印。在 Java 中,我将使用 Lombok @ToString
注释来解决这个问题。
问题:
有什么方法可以在 Kotlin 中实现吗?
推荐的方式是手动写toString
(或者IDE生成)希望大家不要有太多这样的类.
data class
的目的是为了适应 85% 的最常见情况,剩下 15% 给其他解决方案。
像你一样,我习惯在 toString()
和 Java 中使用 lombok equals()
,所以有点失望的是 Kotlin 中需要非数据 类所有标准样板文件。
所以我创建了 Kassava,一个开源库,让您无需任何样板即可实现 toString()
和 equals()
- 只需提供属性列表即可!
例如
// 1. Import extension functions
import au.com.console.kassava.kotlinEquals
import au.com.console.kassava.kotlinToString
import java.util.Objects
class Employee(val name: String, val age: Int? = null) {
// 2. Optionally define your properties for equals()/toString() in a companion
// object (Kotlin will generate less KProperty classes, and you won't have
// array creation for every method call)
companion object {
private val properties = arrayOf(Employee::name, Employee::age)
}
// 3. Implement equals() by supplying the list of properties to be included
override fun equals(other: Any?) = kotlinEquals(
other = other,
properties = properties
)
// 4. Implement toString() by supplying the list of properties to be included
override fun toString() = kotlinToString(properties = properties)
// 5. Implement hashCode() because you're awesome and know what you're doing ;)
override fun hashCode() = Objects.hash(name, age)
}
我发现 Apache Commons Lang 的 ToStringBuilder
with reflection 很有用,但它会在我不需要时调用 hashCode()
和其他方法(还有一个调用 hashCode()
来自 3rd- party lib 生成一个 NPE)。
所以我选择了:
// class myClass
override fun toString() = MiscUtils.reflectionToString(this)
// class MiscUTils
fun reflectionToString(obj: Any): String {
val s = LinkedList<String>()
var clazz: Class<in Any>? = obj.javaClass
while (clazz != null) {
for (prop in clazz.declaredFields.filterNot { Modifier.isStatic(it.modifiers) }) {
prop.isAccessible = true
s += "${prop.name}=" + prop.get(obj)?.toString()?.trim()
}
clazz = clazz.superclass
}
return "${obj.javaClass.simpleName}=[${s.joinToString(", ")}]"
}
您可以定义一个数据 class,其中包含您要使用的数据,并通过委托给它来实现方法。
使用 Kotlin 反射怎么样?我进入 Kotlin 几天了,很抱歉,如果我误解了问题,或者写了 "Kotlin inefficient" 示例。
override fun toString() : String{
var ret : String = ""
for (memberProperty in this.javaClass.kotlin.memberProperties){
ret += ("Property:${memberProperty.name} value:${memberProperty.get(this).toString()}\n");
}
return ret
}
这也可以在新创建的接口中实现,例如 ToString2Interface as fun toString2。那么所有实现 ToString2Interface 的 类 都将具有 toString2()
这就是我最终要做的。
在 Any
class
fun Any.toStringByReflection(exclude: List<String> = listOf(), mask: List<String> = listOf()): String {
val propsString = this::class.memberProperties
.filter { exclude.isEmpty() || !exclude.contains(it.name) }
.joinToString(", ") {
val value = if (!mask.isEmpty() && mask.contains(it.name)) "****" else it.getter.call(this).toString()
"${it.name}=${value}"
};
return "${this::class.simpleName} [${propsString}]"
}
然后你可以从单个类型调用这个方法。
override fun toString(): String {
return this.toStringByReflection()
}
生成下面的字符串
Table [colums=[], name=pg_aggregate_fnoid_index, schema=pg_catalog, type=SYSTEM INDEX]
名称字段被屏蔽:
override fun toString(): String {
return this.toStringByReflection(mask= listOf("name"))
}
它生成,
Table [colums=[], name=****, schema=pg_catalog, type=SYSTEM INDEX]