Umut Tekin
Umut Tekin

Reputation: 258

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?

Upvotes: 1

Views: 2285

Answers (3)

6rchid
6rchid

Reputation: 1303

You don't cast the ParcelableArrayList into an ArrayList, you create it using an ArrayList when you retrieve it just like how the extra was emitted:

mUserList = ArrayList<UserClass>(getParcelableArrayListExtra(EXTRA_PAYMENT_OPTIONS_EXTRA))

Upvotes: 1

Haris Bhatti
Haris Bhatti

Reputation: 211

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
}

Upvotes: 0

David Wasser
David Wasser

Reputation: 95626

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:

  1. Unparcel the ArrayList into ArrayList<Parcelable>
  2. Check/convert the contents of the ArrayList<Parcelable> into the expected type

Upvotes: 0

Related Questions