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.
Related
I have a BaseViewModel that basically has the function to get the user data like so:
abstract class BaseViewModel(
private val repository: BaseRepository
) : ViewModel() {
private var _userResponse: MutableLiveData<Resource<UserResponse>> = MutableLiveData()
val userResponse: LiveData<Resource<UserResponse>> get() = _userResponse
fun getUserData() = viewModelScope.launch {
_userResponse.value = Resource.Loading
_userResponse.value = repository.getLoggedInUserData()
}
}
In my Fragment, I access this data by just calling viewModel.getUserData(). This works. However, I'd like to now be able to edit the data. For example, the data class of UserResponse looks like this:
data class UserResponse(
var id: Int,
var username: String,
var email: String
)
In other fragments, I'd like to edit username and email for example. How do I do access the UserResponse object and edit it? Is this a good way of doing things? The getUserData should be accessed everywhere and that is why I'm including it in the abstract BaseViewModel. Whenever the UserResponse is null, I do the following check:
if (viewModel.userResponse.value == null) {
viewModel.getUserData()
}
If you want to be able to edit the data in userResponse, really what you're talking about is changing the value it holds, right? The best way to do that is through the ViewModel itself:
abstract class BaseViewModel(
private val repository: BaseRepository
) : ViewModel() {
private var _userResponse: MutableLiveData<Resource<UserResponse>> = MutableLiveData()
val userResponse: LiveData<Resource<UserResponse>> get() = _userResponse
fun setUserResponse(response: UserResponse) {
_userResponse.value = response
}
...
}
This has a few advantages - first, the view model is responsible for holding and managing the data, and provides an interface for reading, observing, and updating it. Rather than having lots of places where the data is manipulated, those places just call this one function instead. That makes it a lot easier to change things later, if you need to - the code that calls the function might not need to change at all!
This also means that you can expand the update logic more easily, since it's all centralised in the VM. Need to write the new value to a SavedStateHandle, so it's not lost if the app goes to the background? Just throw that in the update function. Maybe persist it to a database? Throw that in. None of the callers need to know what's happening in there
The other advantage is you're actually setting a new value on the LiveData, which means your update behaviour is consistent and predictable. If the user response changes (either a whole new one, or a change to the current one) then everything observeing that LiveData sees the update, and can decide what to do with it. It's less brittle than this idea that one change to the current response is "new" and another change is "an update" and observers will only care about one of those and don't need to be notified of the other. Consistency in how changes are handled will avoid bugs being introduced later, and just make it easier to reason about what's going on
There's nothing stopping you from updating the properties of the object held in userResponse, just like there's nothing stopping you from holding a List in a LiveData, and adding elements to that list. Everything with a reference to that object will see the new data, but only if they look at it. The point of LiveData and the observer pattern is to push updates to observers, so they can react to changes (like, say, updating text displayed in a UI). If you change one of the vars in that data class, how are you going to make sure everything that needs to see those changes definitely sees them? How can you ensure that will always happen, as the app gets developed, possibly by other people? The observer pattern is about simplifying that logic - update happens, observers are notified, the end
If you are going to do things this way, then I'd still recommend putting an update function in your VM, and let that update the vars. You get the same benefits - centralising the logic, enabling things like persistence if it ever becomes necessary, etc. It could be as simple as
fun setUserResponse(response: UserResponse) {
_userResponse.value?.run {
id = response.id
username = response.username
email = response.email
}
}
and if you do decide to go with the full observer pattern for all changes later, everything is already calling the function the right way, no need for changes there. Or you could just make separate updateEmail(email: String) etc functions, whatever you want to do. But putting all that logic in the VM is a good idea, it's kinda what it's there for
Oh and you access that object through userResponse.value if you want to poke at it - but like I said, better to do that inside a function in the VM, keep that implementation detail, null-safety etc in one place, so callers don't need to mess with it
The ideal way to update userResponse you should change/edit _userResponse so that your userResponse we'll give you the updated data.
it should be something like this
_userResponse.value = Resource<UserResponse>()
I'm an old Java fan and trying to understand Kotlin basics. Can someone tell me what is the difference between these codes:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
VS
var _users: mutableListOf<User>()
private set
As far as understand, upper code provides set _users only in that class and get it publicly which seems pretty same with the code below but in Google docs they say it's backing property which i don't get what it is.
Both examples are really totally different. You need to understand the difference between:
val MutableList
and:
var List
First makes possible to modify the contents of a list, but you can't replace the list object itself. Second, you can replace the list object, but you cannot modify its contents. This isn't really specific to Kotlin, it is the same in Java. val (or making a setter private) is like final field and List is like wrapping a list with Collections.unmodifiableList() - they're different things.
In your first example, neither the external nor internal code can replace the list, but internal code can modify its contents - external can't. In second example both internal and external code can modify contents of the list, but only internal code can replace the list entirely.
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()
I am trying to learn the "Kotlin native way" to do things in Android, while being an expert in neither Kotlin, Java, nor Android development. Specifically, when to use ArrayList versus MutableList.
It seems to me that MutableList should be chosen whenever possible. Yet, if I look at Android examples, they seem to always choose the ArrayList (as far as I've found so far).
Below is a snippet of a working example that uses ArrayList and extends Java's RecyclerView.Adapter.
class PersonListAdapter(private val list: ArrayList<Person>,
private val context: Context) : RecyclerView.Adapter<PersonListAdapter.ViewHolder>() {
Question 1)
Could I simply write the above code as follows (note MutableList<> instead of ArrayList<>), even though I am borrowing from Android's Java code?
class PersonListAdapter(private val list: MutableList<Person>,
private val context: Context) : RecyclerView.Adapter<PersonListAdapter.ViewHolder>() {
Question 2)
Is it really better to always use MutableList over ArrayList? What are the main reasons? Some of that link I provide above goes over my head, but it seems to me that MutableList is a looser implementation that is more capable of changing and improving in the future. Is that right?
The difference is:
if you use ArrayList() you are explicitly saying "I want this to be an ArrayList implementation of MutableList and never change to anything else".
If you use mutableListOf() it is like saying "Give me the default MutableList implementation".
Current default implementation of the MutableList (mutableListOf()) returns an ArrayList. If in the (unlikely) event of this ever changing in the future (if a new more efficient implementation gets designed) this could change to ...mutableListOf(): MutableList<T> = SomeNewMoreEfficientList().
In that case, wherever in your code you used ArrayList() this will stay ArrayList. Wherever you have used mutableListOf() this would change from ArrayList to the brilliantly named SomeNewMoreEfficientList.
ArrayList is an implementation of the MutableList interface in Kotlin:
class ArrayList<E> : MutableList<E>, RandomAccess
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-array-list/index.html
That answer may indicate that MutableList should be chosen whenever possible, but ArrayList is a MutableList. So if you're already using ArrayList, there's really no reason to use MutableList instead, especially since you can't actually directly create an instance of it (MutableList is an interface, not a class).
In fact, if you look at the mutableListOf() Kotlin extension method:
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
you can see that it just returns an ArrayList of the elements you supplied.
I'm currently assigning numbers to an object for each round of a game. for instance a,b,c,d,e. I save this object in an array and then later use this array to build a recycler view for each new round, so you can scroll through all the rounds and see how you did. What I'm having trouble doing are 2 things. each round you're going to get either 10 * what is in a or -10 * what is in a.
Should I do the math on that and store it in the object, or is it better to get a from the object and do the math on it then?
How should i total the score? should i run the array through a loop each time a round is submitted and total a, or is there a better/easier way to update your total score when each round is submitted?
Here's my object class
class Rounds (
var rn: Int,
var t1score: Int,
var t2score: Int,
)
and my function
private fun funScoreRound(rounds: ArrayList<Rounds>, rn: Int) {
//rn = round, t1score = team one score for the round, t2score = team two score for the round
var rn++
var t1score = t1bid * 10
var t2score = t2bid * 10
rounds.add(Rounds(rn, 1score, t2score))
I tried using
var onetotal = rounds.sumby { t1score }
var twototal = rounds.sumby { t2score }
but that does not total how i expected. Just really looking for what the best practice is, if it's to save it to a shared preference and then every time a round is submitted resave it and reupdate the text field, or ...
I am not very sure of what you are asking, but i am trying answer the question you have listed out.
You can construct an custom class object to store all rounds information and do the math inside, lets say MatchSummary or any name you like. By doing this, you can easily manage each match / round you have.
data class MatchSummary(private val rounds: ArrayList<Rounds>) {
fun getTeamOneTotalScore(): Int = rounds.sumBy{ t1score }
fun getTeamTwoTotalScore(): Int = rounds.sumBy{ t2score }
fun getAllRounds(): ArrayList<Rounds> = rounds
fun getCurrentRound(): Int = rounds.size
fun getRoundsInfo(index: Int): Rounds = rounds.get(index)
fun addRounds(t1bid: Int, t2bid: Int) {
// do the math here
let t1RoundScore = t1bid * 10
let t2RoundScore = t2bid * 10
rounds.add(Rounds(rounds.size+1, t1RoundScore, t2RoundScore))
}
}
sumBy function should be the function you are looking for. Could you please tell what difficulties you have in using it?
First of all, three of them can co-exist. They have different purpose and usage. Storing inside an array (memory) is the typical way for your application to operate correctly. You cannot persist the data after application closed.
Storing inside sharedPreference or database are ways to persist data upon application close and launch. The difference is the size of the data you want to persist. SharePreference is made for developer to save the user preferences / settings which is tiny.
Database is made for developer to persist the data will be shown to user which is larger in size. Apart from that, you can share the data between different apps.
Hope i can provide some idea/hints of what you are trying to do. Happy coding!
Reference:
SharePreferences
Room Persistence Library