生成toString方法以包括基类的成员吗? [英] Generated toString method to include members from base class?

查看:105
本文介绍了生成toString方法以包括基类的成员吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Kotlin中构建一个DTO(数据传输对象)类,以从Java应用程序中调用它.给出以下代码:

I am trying to build a DTO (data-transfer-object) classes in Kotlin to be called from a Java application. Given following code:

BaseDto.kt

package sandbox.KotlinDataObjects

import org.apache.commons.lang3.builder.ToStringBuilder
import java.sql.ResultSet
import java.sql.SQLException

/**
 * Base DTO
 * (JSON/XML serialization code removed for clarity)
 */
abstract class BaseDto(
        var id: Long = 0
) {
    @Throws(SQLException::class)
    open protected fun fromResultSet(rs: ResultSet) {
        this.id = rs.getLong("id")
    }

    /**
     * Override toString and use StringBuilder
     * Since DataObject may be one of many objects based on BaseDto
     *   we want to avoid doing this in every data class
     * Derived class seems to generate a toString and this never gets
     *   called, this is the issue I believe
     */
    override fun toString(): String {
        return ToStringBuilder.reflectionToString(this)
    }
}

DataObject.kt

package sandbox.KotlinDataObjects

import org.apache.commons.lang3.builder.ToStringBuilder
import java.sql.ResultSet

data class DataObject(
        var name: String = ""
) : BaseDto() {
    override fun fromResultSet(rs: ResultSet) {
        super.fromResultSet(rs)
        name = rs.getString("name")
    }
}

Main.java

package sandbox.KotlinDataObjects;

import org.mockito.Mockito;

import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {
    /**
     * Mock ResultSet for testing with columns we are requesting
     * @return ResultSet
     * @throws SQLException
     */
    private static ResultSet getMockedResultSet() throws SQLException {
        ResultSet mockedRs = Mockito.mock(ResultSet.class);
        Mockito.when(mockedRs.getLong("id")).thenReturn(12L);
        Mockito.when(mockedRs.getString("name")).thenReturn("MyName");
        return mockedRs;
    }

    public static void main(String[] args) {
        try (ResultSet mockedRs = getMockedResultSet()) {

            // Read DataObject from ResultSet (mocked to avoid DB connection)
            DataObject dobj = new DataObject();
            dobj.fromResultSet(mockedRs);

            System.out.println("toString: dobj="+dobj);
            System.out.println("getters: dobj.name="+dobj.getName()+"  dobj.id="+dobj.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

这将显示:

toString: dobj2=DataObject(name=MyName)
getters: dobj2.name=MyName  dobj2.id=12

我想在DataObject.toString调用中包括BaseDto中的 id 字段.一种选择是使用DataObject.toSting中的ToStringBuilder.reflectionToString覆盖toString,但必须将其添加到从BaseDto派生的每个对象中.有没有一种方法可以声明一个数据对象层次结构,该层次结构自动包括基类中的成员.在Java中,这可以通过在基础中重写toString()并使用ToStringBuilder.reflectionToString来实现.在Kotlin中,不调用基础toString,因为数据对象会生成自己的toString版本,而不会调用super.

I would like to include the id field form the BaseDto in the DataObject.toString call. One option is to override the toString with ToStringBuilder.reflectionToString in DataObject.toSting, but it would have to be added to every object derived from BaseDto. Is there a way to declare a data object hierarchy that automatically includes members from base classes. This is doable in java by overriding toString() in the base and using the ToStringBuilder.reflectionToString, in Kotlin the base toString is not called since data object generates its own version of toString which does not call super.

推荐答案

使用如下所示的反射调用覆盖BaseDto中的toString(

Override toString in BaseDto with a reflection call like the following (credit to @junique for the reflectionToString() code example)...

import kotlin.reflect.*
import kotlin.collections.*
import java.lang.reflect.Modifier

abstract class BaseDto(
        var id: Long = 0L
) {
    open protected fun fromResultSet(rs: ResultSet) {
        this.id = rs.id
    }

    override open public fun toString() = reflectionToString(this)

    fun reflectionToString(obj: Any?): String {
        if(obj == null) {
            return "null"
        }
        val s = mutableListOf<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而不是data class对此进行了测试.对于data class无效.在1.1.之前,data classes甚至不能扩展其他类(但是它们可以实现接口).我越来越同意OP.

I tested this by creating a regular class instead of a data class. It doesn't work for a data class. Prior to 1.1., data classes could not even extend other classes (but they could implement interfaces). More and more, I am agreeing with the OP.

这篇关于生成toString方法以包括基类的成员吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆