
Safe/unsafe cast operator
In strongly typed languages, such as Java or Kotlin, we need to convert values from one type to another explicitly using the cast operator. A typical casting operation is taking an object of one particular type and turning it into another object type that is its supertype (upcasting), subtype (downcasting), or interface. Let's start with a small reminder of casting that can be performed in Java:
Fragment fragment = new ProductFragment(); ProductFragment productFragment = (ProductFragment) fragment;
In the preceding example, there is an instance of ProductFragment that is assigned to a variable storing the Fragment data type. To be able to store this data in the productFragment variable that can store only the ProductFragment data type, so we need to perform an explicit cast. Unlike Java, Kotlin has a special as keyword representing the unsafe cast operator to handle casting:
val fragment: Fragment = ProductFragment() val productFragment: ProductFragment = fragment as ProductFragment
The ProductFragment variable is a subtype of Fragment, the preceding example will work fine. The problem is that casting to an incompatible type will throw the exception ClassCastException. That's why the as operator is called an unsafe cast operator:
val fragment : String = "ProductFragment" val productFragment : ProductFragment = fragment as
ProductFragment
// Exception: ClassCastException
To fix this problem, we can use the safe cast operator as?. It is sometimes called the nullable cast operator. This operator tries to cast a value to the specified type, and returns null if the value cannot be cast. Here is an example:
val fragment: String = "ProductFragment" val productFragment: ProductFragment? = fragment as?
ProductFragment
Notice that usage of the safe cast operator requires us to define the name variable as nullable (ProductFragment? instead of ProductFragment). As an alternative, we can use the unsafe cast operator and nullable type ProductFragment?, so we can see exactly the type that we are casting to:
val fragment: String = "ProductFragment" val productFragment: ProductFragment? = fragment as
ProductFragment?
If we would like to have a productFragment variable that is non-nullable, then we would have to assign a default value using the elvis operator:
val fragment: String = "ProductFragment" val productFragment: ProductFragment? = fragment as?
ProductFragment ?: ProductFragment()
Now, the fragment as? ProductFragment expression will be evaluated without a single error. If this expression returns a non-nullable value (the cast can be performed), then this value will be assigned to the productFragment variable, otherwise a default value (the new instance of ProductFragment) will be assigned to the productFragment variable. Here is a comparison between these two operators:
- Unsafe cast (as): Throws ClassCastException when casting is impossible
- Safe cast (as?): Returns null when casting is impossible
Now, when we understand the difference between the safe cast and unsafe cast operators, we can safely retrieve a fragment from the fragment manager:
var productFragment: ProductFragment? = supportFragmentManager .findFragmentById(R.id.fragment_product) as? ProductFragment
The safe cast and unsafe cast operators are used for casting complex objects. When working with primitive types, we can simply use one of the Kotlin standard library conversion methods. Most of the objects from the Kotlin standard library have standard methods used to simplify common casting to other types. The convention is that this kind of function has a prefix to, and the name of the class that we want to cast to. In the line in this example, the Int type is cast to the String type using the toString method:
val name: String val age: Int = 12 name = age.toString(); // Converts Int to String
We will discuss primitive types and their conversions in the primitive data types section.