ArrayList - I can't resolve this red line code - android

before the code as follows
class MainAdapter(val locationList: ArrayList<HashMap<String,String>>): RecyclerView.Adapter<MainAdapter.ViewHolder>() {
val byDates = locationList.groupBy { it["time"] } //this fine
}
after that i change to use model
class MainAdapter(val locationList: ArrayList<Model>): RecyclerView.Adapter<MainAdapter.ViewHolder>() {
val byDates = locationList.groupBy { it["time"] } //this red line i cant resolve
}
and this my model
data class Model(val name : String?,
val address : String?,
val time : String?)

It's because in the first (working) example it refers to an instance of HashMap<String, String>, thus calling it["time"] is actually equal to calling it.get("time").
In the second example however, it refers to Model. In Kotlin, you cannot access properties by "brackets" syntax (like in JS for example), thus it shows an error. The correct code would be locationList.groupBy { it.time }.

Related

getParcelableArrayListExtra causes a different type to be set to a variable

The problem starts with getParcelableArrayListExtra doesn't support type check when we try to set it to a variable. Let me give an example as basic as I can.
A User Class.
import kotlinx.parcelize.Parcelize
import android.os.Parcelable
#Parcelize
data class UserClass(
var name: String? = null,
var text: String? = null,
var age: Int? = null
) : Parcelable
The random class which we'll try to set to the User variable.
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
#Parcelize
data class MessageClass(
val title: String?, = Constant.STRING_EMPTY
val text: String? = Constant.STRING_EMPTY
) : Parcelable
The class that fills intent
class FillIntentClass(){
//Let's say one of the developers added the MessageClass object inside our intent.
//Or BE sent the wrong type of object and I passed its value to the intent.
private fun DummyFunctionToSetIntent(){
val messageList = arraylistOf(MessageClass(title = "hello",text ="dummy text")
intent.putParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA, messageList)
}
}
Test class
class MyTestClass(){
// UserList variable
private var mUserList: ArrayList<UserClass>? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
with(intent) {
// In this situation, mUserList became the type of ArrayList<MessageClass>
// But it shouldn't be possible. Because it must accept only ArrayList<UserClass>
// And that causes mostly crashes when the other code parts use it.
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA)
// mUserList now pretend its like ArrayList<MessageClass>. But i set it as ArrayList<UserClass> at the top of the class.
// The best way to solve this is to type check with as?. If the type is not as expected it must return null.
// But I cannot use type check here. It gives me a "Not enough information to infer type variable T" error.
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA) as? ArrayList<UserClass> //(compile error here on IDE)
// So I had to come out with the below solution. But I cannot say it's the best practice.
if (getParcelableArrayListExtra<UserClass>(EXTRA_PAYMENT_OPTIONS_EXTRA)
?.filterIsInstance<UserClass>()?.isNotEmpty() == true
) {
mUserList = getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA)
}
}
}
}
Type check(as,as?) works with getParcelable functions as expected. But when it comes to the getParcelableArrayListExtra it just doesn't work and gives compile error as I explained above.
Do you have any knowledge of what's the best option for as, as? check? And how it's possible for mUserList to accept a different type of Array and pretend like it?
This is a mess for a few reasons:
You are coding in Kotlin, but the classes you are dealing with (Parcelable, Bundle, Intent, ArrayList) are actually Java
Generics in Java are a hack
I would split the problem into 2 parts:
Unparcel the ArrayList into ArrayList<Parcelable>
Check/convert the contents of the ArrayList<Parcelable> into the expected type
Check the API level and code accordingly:
if (Build.VERSION.SDK_INT >= 33) {
data = intent.getParcelableExtra (String name, Class<T> clazz)
}else{
data = intent.getParcelableExtra("data")
}
Also you can use these extensions for bundle and intent:
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? = when {
SDK_INT >= 33 -> getParcelableExtra(key, T::class.java)
else -> #Suppress("DEPRECATION") getParcelableExtra(key) as? T
}
inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? = when {
SDK_INT >= 33 -> getParcelable(key, T::class.java)
else -> #Suppress("DEPRECATION") getParcelable(key) as? T
}

get the value of a parameter from a data class in kotlin

So, i'm pretty new to kotlin and still learning stuff, I have a data class named Country with 4 parameters
County(name:String, policePhone:String, ambulancePhone:String,
firebrigadePhone:String)
, a listOf Country with 27 objects in it and a var nameC1 taken from the MainActivity.
I've called the list method forEach and I want to confront every name in the list with the variable nameC and when a match is found execute some code.
data class Country(val name: String, val police:String, val ambulance:String,val firefighter:String) {
}
var nameC1 = (activity as MainActivity).nameC
val numberList= listOf<Country>(
Country("Austria","133","144","122"),
Country("Belgium","101","100","100"),
Country("Bulgaria","166","150","160"),
Country("Croatia","192","194","193"),
Country("Cyprus","199","199","199"),
Country("Czech Republic","158","155","150"),
Country("Denmark","112","112","112"),
Country("Estonia","112","112","112"),
Country("Finland","112","112","112"),
Country("France","17","15","18"),
Country("Germany","110","112","112"),
Country("Greece","100","166","199"),
Country("Hungary","107","104","105"),
Country("Ireland","112","112","112"),
Country("Italy","113","118","115"),
Country("Latvia","112","112","112"),
Country("Lithuania","02","03","01"),
Country("Luxembourg","113","112","112"),
Country("Malta","112","112","112"),
Country("Netherlands","112","112","112"),
Country("Poland","997","999","998"),
Country("Portugal","112","112","112"),
Country("Romania","112","112","112"),
Country("Slovakia","158","155","150"),
Country("Slovenia","113","112","112"),
Country("Spain","092","061","080"),
Country("Sweden","112","112","112")
)
numberList.forEach { if (Country.name==nameC1 ) }
// i'm expecting String1==String2 but i'm
//stuck here because it says name is an unresolved reference
}
I'd use a getName() but i know in kotlin getter/setter are automated ( I'm not used to it) and ihaven't found anything useful on the kotlin doc. site,
I've seen on this site that someone suggested to implement Kotlin-reflection but I don't understand how I'm not supposed to get a parameter from a class by default.
forEach creates a lambda for each of the element in the collection. The default name for the element inside the lambda is it. But you can rename it to something else too. Refer to the doc
Here is a working example of your code
data class Country(val name: String, val police:String, val ambulance:String,val firefighter:String)
fun doThis(nameC1: String) {
val numberList= listOf<Country>(
Country("Austria","133","144","122"),
Country("Belgium","101","100","100"),
Country("Bulgaria","166","150","160"),
Country("Croatia","192","194","193"),
Country("Cyprus","199","199","199"),
Country("Czech Republic","158","155","150"),
Country("Denmark","112","112","112"),
Country("Estonia","112","112","112"),
Country("Finland","112","112","112"),
Country("France","17","15","18"),
Country("Germany","110","112","112"),
Country("Greece","100","166","199"),
Country("Hungary","107","104","105"),
Country("Ireland","112","112","112"),
Country("Italy","113","118","115"),
Country("Latvia","112","112","112"),
Country("Lithuania","02","03","01"),
Country("Luxembourg","113","112","112"),
Country("Malta","112","112","112"),
Country("Netherlands","112","112","112"),
Country("Poland","997","999","998"),
Country("Portugal","112","112","112"),
Country("Romania","112","112","112"),
Country("Slovakia","158","155","150"),
Country("Slovenia","113","112","112"),
Country("Spain","092","061","080"),
Country("Sweden","112","112","112") )
numberList.forEach {
if (it.name == nameC1) {
println("Match")
}
}
}
fun main() {
doThis("Slovenia")
}
Try it for yourself on play.kotlinlang.org - Link
The above code will execute the println function when the condition is true.
In the forEach loop you have to use it to access the name parameter.
like this
numberList.forEach { if (it.name==nameC1 )}
Try with the following code. You can apply filter on list
//if you want iterate your list try with below code
numberList.forEach {
val name = it.name
val police = it.police
}
//If you want apply filter on list take reference from below code
private var countryList: ArrayList<Country> = arrayListOf(
Country("Austria", "133", "144", "122"),
Country("Belgium", "101", "100", "100")
)
val searchList = countryList.filter { country-> country.name == nameC1}

Kotlin apply function with data class

I have a data class like this -
data class User(val id: String, val name: String)
I want to use apply scope function to modify the values and return the modified object.
User("1", "Name").apply {
this.id = "2" // Gives me error since val cannot be assigned
}
I do not want to make my data class immutable and also don't want to use copy function. How do I fix this?
You can use copy method with data classes:
val user1 = User("1", "Name")
val user2 = user1.copy(id = "2")
change like this data class User(var id: String, var name: String)

When returning Generic type * I get error not enough information to infer type variable T

I have the following code -
// My function -
suspend fun leaveGroup(isSoloGroup: Boolean, groupMemberEntity: GroupMemberEntity): Resource<*> {
val snapShot = remoteDataSource.getSnapshot(GROUP_MEMBERS_COLLECTION) {
whereEqualTo(Constants.DatabaseProperties.ID, groupMemberEntity.id)
}
if (snapShot is Resource.Exception) {
return snapShot
}
val delete = remoteDataSource.deleteSnapshot(snapShot.data!!)
if (delete is Resource.Exception) {
return delete
}
if (isSoloGroup.not()) {
return (Resource.Success()) // This is where the error occurs
}
// Code continues ...
}
//My resource class -
sealed class Resource<T> {
abstract val data: T?
data class Success<T>(override val data: T? = null) : Resource<T>()
data class Exception<T>(val throwable: Throwable, override val data: T? = null) : Resource<T>()
data class Loading<T>(val hasStarted: Boolean = false, override val data: T? = null) : Resource<T>()
}
And for some reason when I am returning the type Resource<*> the class Resource. Success can't be instantiated with its default value null and I must explicitly give it a null value - otherwise I will get the error saying Not enough information to infer type variable T. Giving it null in the constructor obviously works, but this is not Clean Code and makes the default value redundant. What am I missing?
There is a Nothing type in the type system, which has no values, it cannot be instantiated.
Because Nothing has no values, Nothing? is actually the type that captures only the null value in Kotlin.
That is, when you declare something like this:
val someVariable = null
The inferred type of someVariable is Nothing?
So if you wish to store the null, you have to give it a type (compiler cannot infer it directly as it can be anything like String?, Int?, etc.), So giving it explicit Nothing? is more appropriate.
if (isSoloGroup.not()) {
return Resource.Success<Nothing>()
}

In which situation val/var is necessary in Kotlin constructor parameter?

Right code:
class MainActHandler(val weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code:
class MainActHandler(weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code screenshot
The only difference is the "val" has been deleted and cannot be resolve.
Which might be important is that it's a inner class.
BUT
This one class without "val/var" in constructor parameter is working:
class BookInfo(convrMgr: ConversationMgr, id: String, queue: RequestQueue, queueTag:String) {
val TAG = "BookInfo"
var title: String? = ""
init {
val url = "https://api.douban.com/v2/book/$id"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
Log.d(TAG + " Response", response.substring(0))
// Parse JSON from String value
val parser = Parser()
val jsonObj: JsonObject =
parser.parse(StringBuilder(response.substring(0))) as JsonObject
// Initial book title of book properties.
title = jsonObj.string("title")
Log.d(TAG + " Book title", title)
convrMgr.addNewMsg(title)
},
Response.ErrorListener { error -> Log.e(TAG + " Error", error.toString()) })
// Set the tag on the request.
stringRequest.tag = queueTag
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
}
And if I add var/val before "queue: RequestQueue", I'll get suggestion:
"Constructor parameter is never used as a property less. This inspection reports primary constructor parameters that can have 'val' or 'var' removed. Unnecessary usage of 'val' and 'var' in primary constructor consumes unnecessary memory."
I am just confused about it.
When you write val/var within the constructor, it declares a property inside the class. When you do not write it, it is simply a parameter passed to the primary constructor, where you can access the parameters within the init block or use it to initialize other properties. For example,
class User(val id: Long, email: String) {
val hasEmail = email.isNotBlank() //email can be accessed here
init {
//email can be accessed here
}
fun getEmail(){
//email can't be accessed here
}
}
Constructor parameter is never used as a property
This suggestion is saying that you do not use this property in place apart from the initialization. So, it suggests you to remove this property from the class.
Constructor parameters must use var or val when they are used as a property elsewhere in the class. They do not need to be properties if they are only used for class initialization.
In the example below, the parameter must be a property (var or val) because it is used in a method:
class A(val number: Int) {
fun foo() = number
}
In this other example, the parameter is only used to initialize the class, so it does not need to be a property:
class B(number: Int): A(number) {
init {
System.out.println("number: $number")
}
}
This might be a late answer but the magic lies under the hood:
Based on #BakaWaii's answer:
Putting var/val will make the variable a property of the class and not putting it will make it a parameter of only the constructor function.
So what does it mean, to understand lets look into some code:
class Test(a: Int){}
Now Lets see the decompiled java code:
public final class Test {
public Test(int a) {
}
}
So now if I try to access a using the object of Test() like the below code:
Test t = new Test(10);
t.a //Error
It will give me error. Unresolved reference: a. Why because a is a parameter of the constructor only.
Now if we put var/val in the paramater like below:
class Test(var a: Int){}
The decompliked Java code will become:
public final class Test {
private int a;
public final int getA() {
return this.a;
}
public final void setA(int var1) {
this.a = var1;
}
public Test(int a) {
this.a = a;
}
}
Thus it will not only give you a class property but also give you getter/setters for setting the values.
Now the next question arises if the field a is private how can it be accessed. Simple answer in Java you cannot, i.e. if you are calling the KT class from a Java you will not be able to assign value of a like Test(1).a = 10 but will have to use Test(1).setA(5).
But as kotlin internally handles getters/setters Test(1).a = 5 will be ok.
For #Parcelize to work you need to open up the super's properties and override them in the child:
abstract class Goal(open var number: Int, open var name: String) : Parcelable
#Parcelize
class OperationalGoal(override var number: Int, override var name: String, var description: String) : Goal(number, name)```
In very simple terms, use var or val in class constructor parameters when you want to use that variable, say, inside a method within that class. Thus you're effectively turning them into properties and not just mere constructor or method parameters.
class User(var name: String, age: Int) {
var str = "John"
var num = 18
fun setName(){
name = str // due to using var on our class constructor parameter, we can access the constructor variable *name* inside this setter method. *name* is a property parameter thanks to the var keyword.
}
fun setAge(){
age = num // this will result in a compiler error, because *age* is just a parameter, notice that var wasn't used in the *age* parameter within the class constructor, which means we can't access it like we did with *name*
}
}
Run this Kotlin Playground code to get a clearer idea of what's going on.

Categories

Resources