I have a data class named "DATA" then I made an ArrayList variable like this:
var listData : ArrayList<DATA>
but I need to change a variable in a certain position but I don't know how to change it. If in Java we can just use setter and getter. How to change a specific variable form data class?
First (sorry I have to stay this) classes shouldn't be all caps :-) Usually, in naming conventions, this is applied to constant values. Classes are named with the first letter as capital only (just a recommendation if you are ever going to apply for pro android dev position.). Second, data classes are like much simpler Java's AutoValue generated objects. Their values are immutable on purpose. You can't change a value inside a data class. It is not possible in Kotlin. If there are changes needed in values inside data classes, they usually happen while mapping to another data class. Let's say when mapping a network deserialised data class object to domain layer data class object. (DataResponse -> Data). So if you need to change it, you can allocate the value of the data class to some local variable and then change it, or map it to another data class.
It looks like you want to insert a new data in a specific position in the array?
Given you have your data class defined like this:
data class MyDataClass(var listData : ArrayList<Data>)
class Data
You just need to:
val myDataClass = MyDataClass(arrayListOf())
val someNewData = Data()
val index = 0
myDataClass.listData[index] = someNewData
Try this,
var dataObject = DATA();
dataObject.listData[index] = "Your data";
Related
While writing code for RecyclerView to get data I figured out there's a data class in Kotlin.
Following codes are taken from two different projects which are linked above.
#Serializable
data class MarsPhoto(
val id: String,
#SerialName(value = "img_src")
val imgSrc: String
)
class Contacts {
#SerializedName("country")
private val country:String? = null
fun getCountry():String?{
return country
}
}
I know that both classes are doing same job. So what does differentiate them? I also wonder in the MarsPhoto data class how they can get the id without declaring SerialName just the way they did for imgSrc. (I am just on the way to learning kotlin now, so I'm absolute beginner).
Basically for "data" class the compiler automatically derives the following members from all properties declared in the primary constructor:
equals()/hashCode() pair
toString() of the form "MarsPhoto(id=1, imgSrc=asdf)"
componentN() functions corresponding to the properties in their order of declaration.
copy()
You can read a lot more at enter link description here
On the SerializedName part of your question. if you are dealing with Gson lib by default it is using fields name as "SerializedName". And only if you want to use something different then field name, you can use SerializedName annotation and pass your custom value there. But usually, everybody just writes #SerializedName() with duplication of field names as value for every field.
It's a good idea if you are receiving and Serializing data from server from Json. Because Backend developers can use a bad keys in response, which you don't want to use in your code, so #SerializedName will be the only place where you will have to see this key, and you can name your fields however you like.
#Serializable used to mark class as serializable to disk or like into a file( alternative is Parcel able in android) special useful in case of process death or configuration changes and #SerializedName("country") used for json parsing when u receive the response from server
You get the id without #SerializedName because the JSON property field is the same as your variable name, but imgSrc and img_src is not. Still, even if they are the same, you should always use #SerializedName, because your variable names could be converted to random letters during code optimization, and obfuscation.
I'm trying to use a room entity with a value class:
#JvmInline
value class UserToken(val token: String)
and the entity:
#Entity(tableName = TABLE_AUTH_TOKEN)
data class TokenEntity(
#PrimaryKey val id: Int = 0,
val token: UserToken
)
I get the following error:
error: Entities and POJOs must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
public final class TokenEntity {
^
is it even possible to use room with value class? I couldn't find anything about this. thanks
See the comment from #CommonsWare. Android does not yet support value classes for Room.
The same holds true for the value classes introduced in kotlin 1.5. The type is not supported.
— Support Inline class in Room entity
Here is a possible explanation according to Kotlin Inline Classes in an Android World.
Looking to solve this you could try and add a TypeConverter for your Inline class, but since your Inline class is just the value it wraps when it’s compiled, this doesn’t make much sense and it doesn’t work as you’d expect even if you tried...
I’m just guessing it’s because this is a TypeConverter converting UserId to Int which is basically the same as Int to Int 😭. Someone will probably solve this problem, but if you have to create a TypeConverter for your Inline class then you are still plus one class for the count (multidex). 👎
I think yes if you can provide a type converter for it to change it to some sort of primitive data type (int , string, long ...etc) when it needs to be stored, and to change it back to its class type when it's fetched from database.
You can read about Type Converters from here
Referencing complex data using Room
other than that, your other class should be an entity and bind both your entities together using a Relation.
at least that's what I know about how to use Room.
UserToken always will have only one attribute? In this case, you don't need two classes, just use token: String directly on your entity class;
If you really need keep this class, you have two options:
TypeConverter, where you basically will convert the object into a json, and save as string in the database;
Relation, where you will transform the UserToken in a entity, and on TokenEntity save the tokenId.
I'm trying to implement JSON parsing in my Android application written in Kotlin using com.squareup.moshi (v1.10.0).
Within the JSON file there are some properties that are not interesting in my case. Let's say, I only need the position to be able to mark the place on a map and the JSON looks like this:
"location":{
"address":{
"country":"..."
},
"position":{
"lat":47.469866,
"lon":19.062435
}
}
If I'm right, the data class in Kotlin should look like this if I'd like to parse that JSON:
#Parcelize
data class Location(
val address: Address,
val position: Position
): Parcelable
#Parcelize
data class Address(
val country: String
): Parcelable
#Parcelize
data class Position(
val lat: Double,
val lon: Double
): Parcelable
In Moshi's documentation I could find the transient keyword to skip values which in Kotlin works as an annotation (#Transient). As the documentation says:
Transient fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead it will get a default value.
Does it mean that if I don't want to have the address object, I should use the following code?
#Parcelize
data class Location(
#Transient val address: Address? = null,
val position: Position
): Parcelable
Also, what about in general terms? What if I have huge list of properties within a JSON object but I know I only need the 'position' object? Do I still have to create null values to parse the JSON file field-by-field?
I think you are looking for something similar to GSON's #Expose annotations, wherein all model fields are excluded from parsing except those annotated.
This functionality is currently not available in Moshi, so your current implementation using the #Transient annotation seems to be the most optimal solution. (See Moshi issues conversation here.)
Extra food for thought:
You may also wish to use #IgnoredOnParcel on your transient fields since you are implementing the parcelable interface. (Have a look here for some implementation pointers.)
Alternatively you could separate your data model into 2 models - one for use in your app and one which reflects the server (JSON) schema (just as you have done above). The main data model for your app (which could implement parcelable) would contain only the fields you use (for example, the position field). When you parse your data, you then convert that data to your primary data model using some simple adapter. (This is often good practice anyhow, since server-side schemas are inherent to change. This way, any changes in the JSON schema wouldn't end having any ripple effect throughout your code.)
https://github.com/square/moshi#omit-fields-with-transient
Omit fields with transient
Some models declare fields that shouldn’t be included in JSON. For example, suppose our blackjack hand has a total field with the sum of the cards:
public final class BlackjackHand {
private int total;
...
}
By default, all fields are emitted when encoding JSON, and all fields are accepted when decoding JSON. Prevent a field from being included by adding Java’s transient keyword:
public final class BlackjackHand {
private transient int total;
...
}
Transient fields are omitted when writing JSON. When reading JSON, the field is skipped even if the JSON contains a value for the field. Instead it will get a default value.
Let's say we have the stock of items stored in the JSON txt file. To access the data we load JSON file using a stringBuilder class, then populate StokItems into:
List<StockItem> stock;
like this:
stock.clear();
Gson gson = new Gson();
stock = gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() {
}.getType());
as we would like to take advantage of the LiveData in Android we introduced:
MutableLiveData<List<StockItem>> stockLiveData = new MutableLiveData<>();
To make it working we have to post data into it:
public LiveData<List<StockItem>> getLiveStockList() {
stockLiveData.postValue(stock);
return stockLiveData;
}
So here is the question, if the line:
stockLiveData.postValue(stock);
consumes memory for creating another copy of
List<StockItem> stock
and if so, could be it's a good idea to keep "original data" inside the
stockLiveData
variable only to reduce the memory impact? (If is it possible ...)
No need to use global variable to hold temporary data. either you can use local variable or directly post to LiveData.
public LiveData<List<StockItem>> getLiveStockList() {
return stockLiveData;
}
And then either
List stock = gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() { }.getType());
stockLiveData.postValue(stock);
or
stockLiveData.postValue(gson.fromJson(stringBuilder.toString(), new TypeToken<List<StockItem>>() { }.getType()))
You can also access the list inside LiveData like below:
stockLiveData.getValue();
Which return the original list data.
There are couple of things about your code I want to point out here:
You do not necessarily need to call postValue in your getter function. As far as there are observers observing from your getter function, you can post value from anywhere.
Even if you use mutable live data that does not necessarily mean that you're allocating memory for or creating a copy of List. When you set or post value on your Mutable Live Data, you're basically referencing the existing list on to value in your Mutable Live Data.
In many samples i see that:
class DataViewModel{
val data:LivaData<Int>
get() = _data
private val _data = MutableLiveData<Int>()
}
But more simply looks like this:
class DataViewModel{
val data = MutableLiveData<Int>()
}
so, why need this more complicated code construction with 2 fields?
It's a practice designed to restrict modification of the value from outside the class.
LiveData is read-only.
MutableLiveData, as the name implies, allows one to change the value it holds.
If you expose a MutableLiveData directly, like in your second example, any code which can access that data field could also modify the value it holds.
Exposing the ability to change data's content from outside DataViewModel class could make it harder to debug and reason about where data's content is coming from at any given time.
MutableLiveData is essentially a LiveData with public access to two methods setValue() and postValue() for modifying that data.
Therefore, MutableLiveData is needed if you plan to modify the values of the LiveData.
However, in programming, it's a common concept to make your variables immutable or restrict the access of those who can modify the data of an object. You wouldn't want to expose the ability to modify the contents of variables within an object if there's no need to do so.
Therefore, for MutableLiveData, we normally use a getter to get it's parent form, which is LiveData.
By getting only LiveData, we can ensure that those who access the LiveData object can only read the values stored within with no ability to change them.
In a sense, it's just the concept of why you should use private variables with getters.