¿Se ha generado el método toString para include miembros de la class base?
Estoy tratando de crear una class DTO (data-transfer-object) en Kotlin para ser llamado desde una aplicación Java. Dado el siguiente código:
BaseDto.kt
- ¿Los modificadores de acceso de Kotlin significan lo mismo que en Java?
- Entendiendo los rasgos en Kotlin
- Tipos de unión / interfaces de extensión
- ¿Es un tipo de propiedad implícita igual con un tipo de propiedad explícito?
- Solución de inheritance básica de Kotlin
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(); } } }
Esto muestra:
toString: dobj2=DataObject(name=MyName) getters: dobj2.name=MyName dobj2.id=12
Me gustaría include el campo de Id del BaseDto en la llamada DataObject.toString. Una opción es anular toString con ToStringBuilder.reflectionToString en DataObject.toSting, pero debería agregarse a cada object derivado de BaseDto. ¿Hay alguna forma de declarar una jerarquía de objects de datos que incluya automáticamente miembros de classs base? Esto es factible en Java sobrescribiendo toString () en la base y usando ToStringBuilder.reflectionToString, en Kotlin no se llama a la base toString ya que el object de datos genera su propia versión de toString que no llama a super.
- ¿Puedo actualizar un object inmutable profundamente nested sin tomar conciencia de su context?
- Anulando la misma firma de diferentes intefaces
- ¿Cuál es el propósito de la class vacía en Kotlin?
- Kotlin - ¿Es posible inicializar el object compañero antes del bloque de inicio en una class?
- Herencia en Kotlin
- Constructor en Kotlin
- Parámetros de Clase abstracta vs. Lambda
- ¿Es una 'propiedad' privada un 'campo'?
Anular toString
en BaseDto
con una llamada de reflexión como la siguiente ( crédito a @junique para el ejemplo del código reflectionToString()
) …
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.declanetworkingFields.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(", ")}]" } }
EDITAR
Probé esto creando una class
regular en lugar de una data class
. No funciona para una data class
. Antes de 1.1, data classes
no podían extender otras classs (pero podían implementar interfaces). Cada vez más, estoy de acuerdo con el OP.