Como criar um construtor vazio para a classe de dados no Kotlin Android

195

Eu tenho mais de 10 parâmetros em uma classe de dados. Quero inicializar a classe de dados com um construtor vazio e definir os valores apenas para alguns parâmetros usando o setter e passar o objeto para o 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 assim será fácil

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

Mas exige que todos os argumentos sejam passados ​​enquanto o construtor criador. Como posso simplificar como acima?

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

Respostas:

245

Você tem 2 opções aqui:

  1. Designe um valor padrão para cada parâmetro principal do construtor :

    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 um construtor secundário que não possui 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>()
                             )
    }

Se você não contar com copyou equalsda Activityclasse ou não usar os Autogenerated data classmétodos em tudo o que você poderia usar classe regular assim:

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

Nem todo DTO precisa ser um data classe vice-versa. De fato, na minha experiência, considero as classes de dados particularmente úteis em áreas que envolvem alguma lógica de negócios complexa.

miensol
fonte
1
Obrigado @miensol, existe alguma maneira de fazer isso usando diversão com cópia? por exemplo. kotlinlang.org/docs/reference/data-classes.html#copying
Sai
Para usar o @SaiKiran, copyvocê precisa de uma instância de classe de dados. Para criá-lo, você precisa chamar um construtor - e aqui estava o problema.
miensol
Estou usando o Kotlin 1.1.2 para o Android Studio 2.3 e o emptyList não está disponível: /
Gonzalo
Deixa pra lá. Não adicionei o kotlin ao meu arquivo de configuração build.gradle.
Gonzalo
3
@Muhammadchhota emptyListnão alocará memória repetidamente. Retorna um singleton .
miensol
70

Se você fornecer valores padrão para todos os campos - o construtor vazio será gerado automaticamente pelo Kotlin.

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

e você pode simplesmente ligar para:

val user = User()
kosiara - Bartosz Kosarzycki
fonte
1
se o id for gerado automaticamente, como usar?
Siddhpura Amit
Trabalhou para mim. Para mensagem de bate-papo do Firebase:class FeelComChatMessage (messageText: String = "", messageUser: String = "")
Jitendra Surve
14

Juntamente com a resposta @miensol, deixe-me adicionar alguns detalhes:

Se você quiser um construtor vazio visível para Java usando classes de dados, precisará defini-lo explicitamente.

Usar valores padrão + especificador de construtor é 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.
}

Isso significa que, com esse truque, agora você pode serializar / desserializar esse objeto com os serializadores Java padrão (Jackson, Gson, etc.).

Gui13
fonte
O último elogio está errado. Pelo menos para o serializador do Gson, de fato, o Gson usa o mecanismo não seguro para criar objetos e ele não chama seu construtor. Acabei de responder uma pergunta relacionada aqui stackoverflow.com/questions/59390294/…
Võ Quang Hòa
6

Se você der um valor padrão para cada parâmetro principal do construtor:

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

e da classe em que as instâncias você pode chamá-la sem argumentos ou com os argumentos que você tem naquele momento

var newItem = Item()

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

Eu sugeriria modificar o construtor primário e adicionar um valor padrão para 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>()
)

Você também pode tornar os valores anuláveis ​​adicionando ?e, em seguida, avaliando 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
)

Em geral, é uma boa prática evitar objetos anuláveis ​​- escreva o código da maneira que não precisamos usá-los. Objetos não anuláveis ​​são uma das vantagens do Kotlin em comparação ao Java. Portanto, a primeira opção acima é preferível .

Ambas as opções fornecerão o resultado desejado:

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

Construtor secundário não vazio para a classe de dados no 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)

Construtor secundário vazio para a classe de dados no 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
fonte
2

A partir da documentação

NOTA: Na JVM, se todos os parâmetros do construtor primário tiverem valores padrão, o compilador gerará um construtor sem parâmetros adicional que utilizará os valores padrão. Isso facilita o uso do Kotlin com bibliotecas como Jackson ou JPA, que criam instâncias de classe através de construtores sem parâmetros.

Gastón Saillén
fonte