Kotlin - генерирует toString() для класса без данных
Ситуация:
У меня класс с lateinit
поля, поэтому их нет в конструкторе:
class ConfirmRequest() {
lateinit var playerId: String
}
Я хотел бы иметь toString()
метод со всеми полями и не хочу писать это вручную, чтобы избежать печати котла. В Java я бы использовал Ломбок @ToString
аннотация для этой проблемы.
Вопрос:
Есть ли способ реализовать это в Котлине?
4 ответа
Рекомендуемый способ написать toString
вручную (или сгенерировать с помощью IDE) и надеяться, что таких классов не так уж много.
Цель data class
это для удовлетворения наиболее распространенных случаев 85%, что оставляет 15% для других решений.
Я нахожу Apache Commons Lang's ToStringBuilder
с отражением полезно, но это вызывает hashCode()
и другие методы, когда мне это не нужно (и один называется hashCode()
из сторонней библиотеки генерирует 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(", ")}]"
}
Как и вы, я привык использовать Ломбок для toString()
а также equals()
в Java был немного разочарован тем, что для классов без данных в 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)
}
Это то, чем я в итоге занимаюсь.
Создать функцию расширения на 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}]"
}
Затем вы можете вызвать этот метод из индивидуального типа.
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]
А как насчет использования отражения Котлина? Я нахожусь в Котлине несколько дней, поэтому извиняюсь, если я неправильно понял вопрос или написал пример "Котлин неэффективен".
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 как fun toString2. Тогда все классы, реализующие ToString2Interface, будут иметь toString2()
Вы можете определить класс данных, который содержит данные, которые вы хотите использовать, и реализовать методы, делегировав это.
/questions/405669/svojstvo-vklyuchaet-isklyuchaet-klassyi-dannyih-kotlin/405682#405682