I wanna duplicate realm object and then change the second, without reassigning all the keys. How can I do this? RealmObject does not have .copy() or .clone() methods.
// Money is not data class and has ∞ fields which will be boring to re-assign
val money = Money()
money.amount = 1000
...
val anotherMoney = money
anotherMoney.amount = 500
println(money.amount) // prints 500
can you please provide more context and appropriate information as I do not see and array's in your code statement. Thank you.
EDIT
Because Money is not a data class, you do not have the auto-generated copy() function available, that leaves you with two options:
Create an custom copy() function in the Money class. This could be mundane if there are huge amount of fields in the class.
Use 3rd party libraries, in which case you'll add external dependency to your RealmObject.
What I will suggest is a no brainer: Try to convert your Money.class to a Data class. You will get auto generated functions and idiomatically it will work as RealmObjects should be key-values pairs.
EDIT
You can use GSON library's serialization/deserialization and hack your way to solve your problem (although this is a hacky way but will do its job):
fun clone(): Money {
val stringMoney = Gson().toJson(this, Money::class.java)
return Gson().fromJson<Money>(stringMoney, Money::class.java)
}
usage:
val originalMoney = Money()
val moneyClone = originalMoney.clone()
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.
As a newbie in Kotlin, i want to ask a question. Let’s say that i have class Dog like below,
data class Dog(val breed : String, val gender : String, val name : String, val age: Int){}
I want to make 1000 instances of this Dog class and add all of instances in an arrayList and later i will use this list in different activities. Imagine that I have an activity which only shows Labradors . So i have to take that arrayList which contains all of my Dogs and filter the breed according to Labrador and show the user.
As i read in articles i think this is an expensive way but i don’t know what the efficient way is to do this. Cause i will create all instances manually like below.
fun dogMaker(){
val dog1 = Dog("example","example","example",1)
val dog2 = Dog("example","example","example",1)
val dog3 = Dog("example","example","example",1)
val dog4 = Dog("example","example","example",1)
val dog5 = Dog("example","example","example",1)
//... goes on...
}
Could you suggest me an efficient way? Thanks in advance.
As a beginner, don't even worry about trying to optimize this. Just use a FOR loop or initialize the array manually and it'll be fine. If you want to access it in different Activities, just make the ArrayList a global variable by declaring it outside of one of your classes. In your case, this means declaring it above MainActivity.
The better but more involved way to do it is to store all these items in a pre-populated database. That way, you're not creating an ArrayList every time the app runs, you're not taking up memory unnecessarily, and you're only accessing what you need at any given time.
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.
So basically up until now I have been using rxjava2 extensively in the applications, but decided to check out data binding, view models and live data. And Im not sure I've got all of this right, because apart from saving state during rotation of device I do not see any other clear benefits of switching, I could even say that I see downsides of introducing data binding with view model between view and rx java powered requests.
Lets see example of some registration form. It would contain:
2 inputs - name and surname
Field with 3 choices
Button with progress
In the reactive world I would have two observables with name and surname, one observable that would merge 3 choices clicks and map them to the right enum, then I could combine all the data together, communicate directly with my single responsible for sending the data in between I would have state with progress or error and tada Im done.
And here is the thing that I came up with using data binding and view models:
class LiveDataViewModel : ViewModel() {
enum class Choice {
NONE, FIRST, SECOND, THIRD
}
private val _progressVisibilityLiveData = MutableLiveData<Boolean>()
private val _errorLiveData = MutableLiveData<GlobalError>()
val progressVisibilityLiveData: LiveData<Boolean> = _progressVisibilityLiveData.apply { value = false }
val errorLiveData: LiveData<GlobalError> = _errorLiveData
val data = LiveDataData()
val observableData = ObservableField(LiveDataData())
fun actionContinue() {
_progressVisibilityLiveData.postValue(true)
if (observableData.get()?.isValid() == false) _errorLiveData.postValue(GlobalError.AllFieldsRequired)
else sendToApi()
}
private fun sendToApi() {
// TODO there would be still an rx java call to single, when we would handle error in the same way we are doing
// it in actionContinue
}
data class LiveDataData(val firstName: ObservableField<String> = ObservableField(""),
val secondName: ObservableField<String> = ObservableField(""),
val choice: ObservableField<Choice> = ObservableField(Choice.NONE)) {
fun changeChoice(newChoice: Choice) {
choice.set(newChoice)
}
fun isValid(): Boolean = !firstName.get().isNullOrEmpty() && !secondName.get().isNullOrEmpty() && choice.get() != Choice.NONE
fun toRequest(): Request = Request(firstName.get()!!, secondName.get()!!, choice.get()!!)
}
}
So I would change fields of my LiveDataData directly from xml using bindData, also I would change state of my selection box depending on this binding too, progress would have to be done manually and then it would trigger the visibility using data binding. But is it really a good way of handling such cases?
The disadvantages I see are that the whole logic in actionContinue would be manually changing values, the values from ObservableProperties could be null, so we either have to handle nullable values everywhere of we have to use !! and to be honest Im not feeling that this is the right direction.
Maybe any of you guys have thought about something similar and could eventually point me if I made some wrong assumptions or if I shouldn't use for example ObservableProperty at all. Obviously there are tons of articles about data binding and live data etc, but I haven't found any that would satisfy my curiosity. Oh and create MutableLiveData for each property from form is not an option.
RxJava is a completely different concept than DataBinding. It's more of a way of handling concurrency than it is about binding data. I 100% think it's worth learning. The Android community has embraced it with open arms.
Shameless plug: I compiled a list of RxJava resources awhile back - http://gregloesch.com/dev/2014/10/20/resources-for-learning-rxjava-android.html
I'm using Kotlin objects to work with my Firebase Database models, as described in the guide. I have many fields that are stored as strings, but really are enums, so to be type-safe I have enum fields in the models, plus a string delegated property that returns the firebase stored value (as suggested in a question I asked some time ago). Now, these fields work if I get/set the string delegate in code, but firebase libs seem to skip them when converting to/from database's json format.
A simple example:
abstract class BaseModel {
#Exclude
open var path: String? = null // fails even if I delete this field!
}
class Weight() : BaseModel() {
constructor(v: Double, u: WeightUnit) : this() {
value = v
unitEnum = u
}
var value: Double = 0.0
#Exclude
var unitEnum: WeightUnit = WeightUnit.KG
var unit: String by EnumStringLowercaseConverter(WeightUnit::class.java).getDelegate(Weight::unitEnum)
}
[...]
val testWeight = Weight(7.0, "kg")
db.getReference("/valid/path/to/save/testWeight").setValue(testWeight)
.addOnSuccessListener { r -> Log.d(LOG_TAG, "set successful") }
.addOnFailureListener { e -> Log.e(LOG_TAG, "set error", e) }
The setValue always gives a Permission Denied error, but works, if I delete unitEnum field and make unit a normal String property.
It's similar for reading: Firebase gives no errors when getting a Weight object, but the weightUnit field is never set to anything else than the default. But, if I manually do weight.unit = "lb", the unitEnum field properly returns WeightUnit.LB.
I'm using firebase libs v10.0.1
Now, the questions:
What can I do to make the delegated properties work correctly with firebase? I can try a different approach to the delegated enum fields, as long as the points from my original question are satisfied (readable, concise and type-safe code).
is there any way to see how exactly do firebase libs convert objects to/from json? Or at least see the converted json? Maybe then I could tweak things myself. Unfortunately, everything firebase-related shows as /* compiled code */ in AndroidStudio.
UPDATE: I could of course add a toMap() method to each model, where I would construct a map containing all the properties needed in firebase, but it would be tiresome to do this for every model, and it solves the saving issue only, the enum fields still wouldn't be set when getting.
The delegated props are also skipped when serializing with GSON. So maybe is there a generic way to make the delegated properties look like regular fields?
Try this code, it should work.
#get:Exclude #set:Exclude
var unitEnum: WeightUnit = WeightUnit.KG
var unit: String
get() = unitEnum.name
set(v) { unitEnum = WeightUnit.valueOf(v) }