Cómo crear un constructor vacío para la clase de datos en Kotlin Android

195

Tengo más de 10 parámetros en una clase de datos, quiero inicializar la clase de datos con un constructor vacío y establecer los valores solo para algunos parámetros usando setter y pasar el objeto al servidor.

data class Activity(
        var updated_on: String,
        var tags: List<String>,
        var description: String,
        var user_id: List<Int>,
        var status_id: Int,
        var title: String,
        var created_at: String,
        var data: HashMap<*, *>,
        var id: Int,
        var counts: LinkedTreeMap<*, *>,
)

Uso:

Algo como esto será fácil

                val activity =  Activity();
                activity.title = "New Computer"
                sendToServer(activity)

Pero requiere que se pasen todos los argumentos mientras se crea el constructor. ¿Cómo puedo simplificar como arriba?

                val activity =  Activity(null,null,null,null,null,"New Computer",null,null,null,null);
                sendToServer(activity)
Sai
fuente

Respuestas:

246

Tienes 2 opciones aquí:

  1. Asigne un valor predeterminado a cada parámetro de constructor primario :

    data class Activity(
        var updated_on: String = "",
        var tags: List<String> = emptyList(),
        var description: String = "",
        var user_id: List<Int> = emptyList(),
        var status_id: Int = -1,
        var title: String = "",
        var created_at: String = "",
        var data: HashMap<*, *> = hashMapOf<Any, Any>(),
        var id: Int = -1,
        var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
    ) 
  2. Declare un constructor secundario que no tiene parámetros:

    data class Activity(
        var updated_on: String,
        var tags: List<String>,
        var description: String,
        var user_id: List<Int>,
        var status_id: Int,
        var title: String,
        var created_at: String,
        var data: HashMap<*, *>,
        var id: Int,
        var counts: LinkedTreeMap<*, *>
    ) {
        constructor() : this("", emptyList(), 
                             "", emptyList(), -1, 
                             "", "", hashMapOf<Any, Any>(), 
                             -1, LinkedTreeMap<Any, Any>()
                             )
    }

Si no confía en copyo equalsde la Activityclase o no usa los data classmétodos autogenerados , puede usar la clase regular de esta manera:

class ActivityDto {
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
}

No todos los DTO deben ser data classay viceversa. De hecho, en mi experiencia, encuentro que las clases de datos son particularmente útiles en áreas que involucran cierta lógica empresarial compleja.

miensol
fuente
1
Gracias @miensol, ¿hay alguna manera de que esto se pueda hacer con copia divertida? p.ej. kotlinlang.org/docs/reference/data-classes.html#copying
Sai
@SaiKiran para usar copynecesita una instancia de clase de datos. Para crearlo, debe invocar un constructor, y aquí estaba el problema.
miensol
Estoy usando Kotlin 1.1.2 para Android Studio 2.3 y emptyList no está disponible: /
Gonzalo
No importa. No agregué kotlin a mi archivo de configuración build.gradle.
Gonzalo
3
@Muhammadchhota emptyListno asignará memoria repetidamente. Devuelve un singleton .
miensol
70

Si proporciona valores predeterminados a todos los campos , Kotlin genera automáticamente un constructor vacío.

data class User(var id: Long = -1,
                var uniqueIdentifier: String? = null)

y simplemente puedes llamar:

val user = User()
kosiara - Bartosz Kosarzycki
fuente
1
si la identificación se genera automáticamente, ¿cómo usarla?
Siddhpura Amit
Trabajó para mi. Para el mensaje de Firebase Chat:class FeelComChatMessage (messageText: String = "", messageUser: String = "")
Jitendra Surve
14

Junto con la respuesta @miensol, permítanme agregar algunos detalles:

Si desea un constructor vacío visible en Java que use clases de datos, debe definirlo explícitamente.

Usar valores predeterminados + especificador de constructor es bastante fácil:

data class Activity(
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
) {
    constructor() : this(title = "") // this constructor is an explicit
                                     // "empty" constructor, as seen by Java.
}

Esto significa que con este truco ahora puede serializar / deserializar este objeto con los serializadores Java estándar (Jackson, Gson, etc.).

Gui13
fuente
El último elogio está mal. Al menos para el serializador Gson, de hecho, Gson usa el mecanismo inseguro para crear objetos y no llamará a su constructor. Acabo de responder una pregunta relacionada aquí stackoverflow.com/questions/59390294/…
Võ Quang Hòa
6

Si le da un valor predeterminado a cada parámetro de constructor primario:

data class Item(var id: String = "",
            var title: String = "",
            var condition: String = "",
            var price: String = "",
            var categoryId: String = "",
            var make: String = "",
            var model: String = "",
            var year: String = "",
            var bodyStyle: String = "",
            var detail: String = "",
            var latitude: Double = 0.0,
            var longitude: Double = 0.0,
            var listImages: List<String> = emptyList(),
            var idSeller: String = "")

y desde la clase donde las instancias puedes llamarlo sin argumentos o con los argumentos que tienes en ese momento

var newItem = Item()

var newItem2 = Item(title = "exampleTitle",
            condition = "exampleCondition",
            price = "examplePrice",
            categoryId = "exampleCategoryId")
yOshi
fuente
3

Sugeriría modificar el constructor primario y agregar un valor predeterminado a cada parámetro:

data class Activity(
    var updated_on: String = "",
    var tags: List<String> = emptyList(),
    var description: String = "",
    var user_id: List<Int> = emptyList(),
    var status_id: Int = -1,
    var title: String = "",
    var created_at: String = "",
    var data: HashMap<*, *> = hashMapOf<Any, Any>(),
    var id: Int = -1,
    var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any, Any>()
)

También puede hacer que los valores sean anulables agregando ?y luego puede evaluar null:

data class Activity(
    var updated_on: String? = null,
    var tags: List<String>? = null,
    var description: String? = null,
    var user_id: List<Int>? = null,
    var status_id: Int? = null,
    var title: String? = null,
    var created_at: String? = null,
    var data: HashMap<*, *>? = null,
    var id: Int? = null,
    var counts: LinkedTreeMap<*, *>? = null
)

En general, es una buena práctica evitar los objetos anulables: escriba el código de la forma en que no necesitemos usarlos. Los objetos no anulables son una de las ventajas de Kotlin en comparación con Java. Por lo tanto, la primera opción anterior es preferible .

Ambas opciones le darán el resultado deseado:

val activity = Activity()
activity.title = "New Computer"
sendToServer(activity)
Micer
fuente
2

Constructor secundario no vacío para la clase de datos en Kotlin:

data class ChemicalElement(var name: String,
                           var symbol: String,
                           var atomicNumber: Int,
                           var atomicWeight: Double,
                           var nobleMetal: Boolean?) {

    constructor(): this("Silver",
                        "Ag", 
                        47,
                        107.8682,
                        true)
}

fun main() {
    var chemicalElement = ChemicalElement()
    println("RESULT: ${chemicalElement.symbol} means ${chemicalElement.name}")
    println(chemicalElement)
}

// RESULT: Ag means Silver
// ChemicalElement(name=Silver, symbol=Ag, atomicNumber=47, atomicWeight=107.8682, nobleMetal=true)

Constructor secundario vacío para la clase de datos en Kotlin:

data class ChemicalElement(var name: String,
                           var symbol: String,
                           var atomicNumber: Int,
                           var atomicWeight: Double,
                           var nobleMetal: Boolean?) {

    constructor(): this("",
                        "", 
                        -1,
                        0.0,
                        null)
}

fun main() {
    var chemicalElement = ChemicalElement()
    println(chemicalElement)
}

// ChemicalElement(name=, symbol=, atomicNumber=-1, atomicWeight=0.0, nobleMetal=null)
Andy Fedoroff
fuente
2

De la documentación

NOTA: en la JVM, si todos los parámetros del constructor primario tienen valores predeterminados, el compilador generará un constructor adicional sin parámetros que utilizará los valores predeterminados. Esto facilita el uso de Kotlin con bibliotecas como Jackson o JPA que crean instancias de clase a través de constructores sin parámetros.

Gastón Saillén
fuente