java Kotlin - 为非数据类生成 toString()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40862207/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Kotlin - generate toString() for a non-data class
提问by awfun
Situation:
情况:
I have a class with lateinit
fields, so they are not present in the constructor:
我有一个带有lateinit
字段的类,因此它们不存在于构造函数中:
class ConfirmRequest() {
lateinit var playerId: String
}
I'd like to have a toString()
method with all fields and don't want to write it manually, to avoid boiler print. In Java I'd use the Lombok@ToString
annotation for this problem.
我想要一个toString()
包含所有字段的方法并且不想手动编写它,以避免锅炉打印。在 Java 中,我会使用Lombok@ToString
注释来解决这个问题。
Question:
问题:
Is there any way to implement it in Kotlin?
有没有办法在 Kotlin 中实现它?
采纳答案by voddan
The recommended way is to write toString
manually (or generate by IDE) and hope that you don't have too many of such classes.
推荐的方式是toString
手动编写(或者IDE生成),希望这样的类不要太多。
The purpose of data class
is to accommodate the most common cases of 85%, which leaves 15% to other solutions.
的目的data class
是容纳 85% 的最常见情况,剩下 15% 留给其他解决方案。
回答by junique
I find Apache Commons Lang's ToStringBuilder
with reflection useful, but it calls hashCode()
and other methods when I don't need that (and one called hashCode()
from a 3rd-party lib generates an NPE).
我发现 Apache Commons Lang 的ToStringBuilder
反射很有用,但是hashCode()
当我不需要它时它会调用和其他方法(hashCode()
从 3rd-party lib调用的方法会生成 NPE)。
So I just go with:
所以我只是去:
// 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(", ")}]"
}
回答by James Bassett
Like you, I was used to using lombok for toString()
and equals()
in Java, so was a bit disappointed that non-data classes in Kotlin required all of the standard boilerplate.
和你一样,我是用来使用的龙目岛toString()
,并equals()
在Java,所以有点失望的是,在科特林非数据类所需的所有标准样板。
So I created Kassava, an open source library that lets you implement toString()
and equals()
without any boilerplate - just supply the list of properties and you're done!
所以我创建Kassava,一个开源库,让你实现toString()
并equals()
没有任何样板-只需提供属性的列表,你就大功告成了!
For example:
例如:
// 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)
}
回答by Duncan McGregor
You can define a data class that contains the data that you want to use and implement methods by delegating to that.
您可以定义一个包含要使用的数据的数据类,并通过委托来实现方法。
回答by vukis
What about using Kotlin reflection? I am into Kotlin for a few days, so apologies, if I misunderstood question, or wrote "Kotlin inefficient" example.
使用 Kotlin 反射怎么样?我进入 Kotlin 几天了,很抱歉,如果我误解了问题,或者写了“Kotlin 效率低下”的例子。
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
}
This can also could be implemented in newly created interface for example ToString2Interface as fun toString2. Then all classes which implements ToString2Interface would have toString2()
这也可以在新创建的接口中实现,例如 ToString2Interface 作为 fun toString2。那么所有实现 ToString2Interface 的类都会有 toString2()
回答by realfire
This is what I endup doing.
这就是我最终要做的。
Create extension function on Any
class
在Any
类上创建扩展函数
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}]"
}
Then you can call this method from individual type.
然后您可以从单个类型调用此方法。
override fun toString(): String {
return this.toStringByReflection()
}
It generates string below
它在下面生成字符串
Table [colums=[], name=pg_aggregate_fnoid_index, schema=pg_catalog, type=SYSTEM INDEX]
With name field masked:
屏蔽名称字段:
override fun toString(): String {
return this.toStringByReflection(mask= listOf("name"))
}
It generates,
它产生,
Table [colums=[], name=****, schema=pg_catalog, type=SYSTEM INDEX]