Jak stworzyć pusty konstruktor dla klasy danych w Kotlin Android

195

Mam klasę 10+ w klasie danych, chcę zainicjować klasę danych za pomocą pustego konstruktora i ustawić wartości tylko dla kilku parametrów za pomocą settera i przekazać obiekt do serwera.

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<*, *>,
)

Stosowanie:

Coś takiego będzie łatwe

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

Ale wymaga to przekazania wszystkich argumentów podczas tworzenia konstruktora. Jak mogę uprościć jak wyżej?

                val activity =  Activity(null,null,null,null,null,"New Computer",null,null,null,null);
                sendToServer(activity)
Sai
źródło

Odpowiedzi:

245

Masz tutaj 2 opcje:

  1. Przypisz wartość domyślną do każdego parametru podstawowego konstruktora :

    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. Zadeklaruj konstruktora dodatkowego, który nie ma parametrów:

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

Jeśli nie liczyć na copylub equalsz Activityklasy lub nie używać wygenerowany automatycznie data classmetod w ogóle można użyć zwykłego klasy tak:

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

Nie każde DTO musi być na data classodwrót. W rzeczywistości z mojego doświadczenia uważam, że klasy danych są szczególnie przydatne w obszarach wymagających złożonej logiki biznesowej.

miensol
źródło
1
Dzięki @miensol, Czy można to zrobić za pomocą kopiowania? na przykład. kotlinlang.org/docs/reference/data-classes.html#copying
Sai
@ SaiKiran do użycia copypotrzebujesz instancji klasy danych. Aby go utworzyć, musisz wywołać konstruktor - i tu był problem.
miensol
Używam Kotlin 1.1.2 dla Androida Studio 2.3, a emptyList jest niedostępny: /
Gonzalo
Nieważne. Nie dodałem kotlin do mojego pliku konfiguracyjnego build.gradle.
Gonzalo
3
@Muhammadchhota emptyListnie będzie wielokrotnie przydzielać pamięci. Zwraca singleton .
miensol
70

Jeśli podasz wartości domyślne dla wszystkich pól - pusty konstruktor jest generowany automatycznie przez Kotlin.

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

i możesz po prostu zadzwonić:

val user = User()
Kosiara - Bartosz Kosarzycki
źródło
1
jeśli identyfikator jest generowany automatycznie, to jak go używać?
Siddhpura Amit
Pracował dla mnie. W przypadku czatu class FeelComChatMessage (messageText: String = "", messageUser: String = "")
Firebase
14

Wraz z odpowiedzią @miensol pozwól mi dodać kilka szczegółów:

Jeśli chcesz widoczny konstruktor w Javie za pomocą klas danych, musisz go jawnie zdefiniować.

Używanie wartości domyślnych + specyfikatora konstruktora jest dość łatwe:

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

Oznacza to, że dzięki tej sztuczce możesz teraz serializować / deserializować ten obiekt za pomocą standardowych serializatorów Java (Jackson, Gson itp.).

Gui13
źródło
Ostatnie polecenie jest złe. Przynajmniej w przypadku serializatora Gson, Gson używa niebezpiecznego mechanizmu do tworzenia obiektów i nie wywoła twojego konstruktora. Właśnie odpowiedziałem na powiązane pytanie tutaj stackoverflow.com/questions/59390294/…
Võ Quang Hòa
6

Jeśli podasz wartość domyślną dla każdego parametru podstawowego konstruktora:

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

oraz z klasy, w której instancje można wywołać bez argumentów lub z argumentami, które masz w danym momencie

var newItem = Item()

var newItem2 = Item(title = "exampleTitle",
            condition = "exampleCondition",
            price = "examplePrice",
            categoryId = "exampleCategoryId")
YOshi
źródło
3

Proponuję zmodyfikować główny konstruktor i dodać wartość domyślną do każdego parametru:

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

Możesz także ustawić wartości zerowalne, dodając, ?a następnie zakładając 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
)

Ogólnie rzecz biorąc, dobrą praktyką jest unikanie obiektów zerowalnych - pisz kod w taki sposób, abyśmy nie musieli ich używać. Niedopuszczalne obiekty są jedną z zalet Kotlina w porównaniu z Javą. Dlatego preferowana jest pierwsza z powyższych opcji .

Obie opcje dają pożądany rezultat:

val activity = Activity()
activity.title = "New Computer"
sendToServer(activity)
Micer
źródło
2

Niepusty pusty konstruktor klasy danych w 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)

Pusty konstruktor wtórny dla klasy danych w 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
źródło
2

Z dokumentacji

UWAGA: W JVM, jeśli wszystkie parametry podstawowego konstruktora mają wartości domyślne, kompilator wygeneruje dodatkowy konstruktor bez parametrów, który użyje wartości domyślnych. Ułatwia to używanie Kotlina z bibliotekami takimi jak Jackson lub JPA, które tworzą instancje klas za pomocą konstruktorów bez parametrów.

Gastón Saillén
źródło