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)

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.


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()

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.).


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")

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)

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)

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.