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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 05:31:06  来源:igfitidea点击:

Kotlin - generate toString() for a non-data class

javakotlintostring

提问by awfun

Situation:

情况:

I have a class with lateinitfields, 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@ToStringannotation 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 toStringmanually (or generate by IDE) and hope that you don't have too many of such classes.

推荐的方式是toString手动编写(或者IDE生成),希望这样的类不要太多。

The purpose of data classis 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 ToStringBuilderwith 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.

您可以定义一个包含要使用的数据的数据类,并通过委托来实现方法。

https://stackoverflow.com/a/46247234/97777

https://stackoverflow.com/a/46247234/97777

回答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 Anyclass

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]