Sunday, 5 June 2022

SAM - Functional Interface


Almost we all of us have seen code like view.setOnClickListener { } or view.setOnLongClickListener { } in Kotlin, and when you click on it to see the source code it will show you the Java OnClickListener interface. Is it a kotlin extension? How can we call our custom listener like this? Let’s see the answer to all of these now.

This syntax is not an extension, it is SAM conversion, SAM stands for Single Abstract Method interface and it’s called also Functional interface which is an interface with only one non-default method (abstract method) and any number of default methods (non-abstract methods), for examples In the JDK we have Runnable class which has only one method called run and in Android SDK we have OnClickListener, OnLongClickListener etc..

How can we create a custom functional interface?

In Kotlin starting from Version 1.4, we can declare a functional interface in Kotlin, we need to use the fun modifier before to the interface keyword.
fun interface MyInterface {
    fun aMethod()
}
Creating a function with the functional interface as param

fun runMyInterface(fi : MyInterface) { ... }


We can pass MyInterface as an anonymous object 

runMyInterface(object : MyInterface {

    override fun aMethod() {

        println("Welcome to www.rajendhiraneasu.in")

    }

})

For functional interfaces, SAM conversion can be achieved through lambda expressions, which makes the code more concise and more readable.  Using lambda expressions can replace manually creating classes that implement functional interfaces. Through SAM conversion, Kotlin can convert any lambda expression whose signature matches the signature of a single abstract method of an interface into an instance of a class that implements the interface.  Please see the above anonymous reference with lambda expressions as below

runMyInterface ({ println("Welcome to www.rajendhiraneasu.in") })


Also, in Kotlin if your last parameter is functional interface you can move your lambda out the brackets ()

runMyInterface {

    println("Welcome to www.rajendhiraneasu.in")

}


Now you can realize that setOnClickListener { } is just because this method takes functional interface which is OnClickListener as a parameter.

Eg: Let’s see with the use case to get the Students Grade of different class levels.
 
// Class Levels
const val PRIMARY_SCHOOL = 1
const val HIGH_SCHOOL = 2

// Data Class holds the student details
data class StudentMarks(val sName: String, val classLevel: Int, val sAvg: Double) {
    fun getStudentGrade(predicate: GradePredicate) = predicate.getGrade((sAvg))
}

// Functional interface (SAM)
fun interface GradePredicate {
    fun getGrade(avg: Double): String
} 

// Grade Predicate definition as lambda 
val primarySchoolGrade = GradePredicate {
    when {
        it > 90 -> "A+"
        it > 80 -> "A"
        it > 65 -> "B"
        it > 45 -> "C"
        it > 34 -> "D"
        else -> "F"
    }
}

val highSchoolGrade = GradePredicate {
    when {
        it > 80 -> "Very Good"
        it > 60 -> "Good"
        it > 34 -> "Fair"
        else -> "Fail"
    }
}

// Lambda expression to select the grade predicate based on the class level 
val gradeSel by lazy { { classLevel: Int -> if (classLevel == PRIMARY_SCHOOL) primarySchoolGrade else highSchoolGrade } }

fun main() {
 val studentList = listOf(
        StudentMarks("Ragavan", PRIMARY_SCHOOL, 92.79),
        StudentMarks("Rajeevan", PRIMARY_SCHOOL, 65.15),
        StudentMarks("Rajeevan", PRIMARY_SCHOOL, 52.23),
        StudentMarks("Arun", HIGH_SCHOOL, 83.21),
        StudentMarks("Harish", HIGH_SCHOOL, 63.56)
    )

    println("Name || Grade")
    studentList.forEach {
        println(
            "${it.sName} ||  ${
                it.getStudentGrade(gradeSel(it.classLevel))
            }"
        )
    }
}
 
Output:



Sunday, 29 May 2022

Passing function as a param to collection filter

It's actually simpler to provide a lambda directly, There's other option that's worth covering, too, which is a function / lambda reference.  If you already had a function that would do the job, you don't need to put it in a lambda, but can refer to it directly, through function or its reference using the :: notation. this will works only if the parameter type(s) are compatible, but it's slightly simpler and can generate slightly more efficient bytecode.


val myValues = listOf(12, 25, 215, 3, 52)
println("All Values: $myValues")
println("Even Filter ${myValues.filter { it % 2 == 0 }} -- Simple implicit lambda")
println("Odd Filter ${myValues.filter { oddFilter(it) }} -- lambda reference as param")
println("odd Filter ${myValues.filter(oddFilter)} -- lambda as param to filter function")
println("Even Filter ${myValues.filter(::evenFilter)} -- function reference as param")

val oddFilter = { a: Int -> a % 2 != 0 }

fun evenFilter(g: Int): Boolean = g % 2 == 0

Output:

All Values: [12, 25, 215, 3, 52]
Even Filter [12, 52] -- Simple implicit lambda
Odd Filter [25, 215, 3] -- lambda reference as param
odd Filter [25, 215, 3] -- lambda as param to filter function
Even Filter [12, 52] -- function reference as param

Happy Coding :-)

Thursday, 14 April 2022

String concatenation using listOfNotNull & joinToString - Kotlin

data class User(val fName: String?, val mName: String?, val lName: String?)
val users = listOf(
        User("Ragavan", null, "V.S."),
        User("Rajeev", "Anbu", "Devan"),
        User("Ravinder", null, null),
        User("", "", ""),
        User(null, null, null)
    )
users.map {
        listOfNotNull(it.fName, it.mName, it.lName)
        .joinToString(" ").trim().takeIf { it.isNotEmpty() } ?: "NA"
}.forEach(::println)

Output:

[Ragavan V.S., Rajeev Anbu Devan, Ravinder, NA, NA]

In the above example here we are using the listOfNotNull method which implicitly handles the null check and ignore the same, then we are easy to concatenate the data using joinToString(...) method. 

Saturday, 5 March 2022

Value Class Kotlin

Value class adds attribute to a value and constraint it’s usage. This class is nothing but a wrapper around a value, but the Kotlin compiler makes sure there is no overhead due to wrapping.

Classes in Kotlin solve two problems:
  1. They convey meaning through their name and make it easier for us to understand what kind of object is passed along.
  2. They enforce type-safety by making sure that an object of class A cannot be passed to a function that expects an object of class B as an input parameter. This prevents serious bugs at compile-time.

Primitive: ❌
Primitive types like Int, Boolean, String or Double also enforce type-safety (you can’t just pass a String where a Boolean is expected), but they don’t really convey a meaning (other than an object being a number of a certain format, for example).

A Double could be pretty much anything: a temperature in degrees Celsius, a weight in kilograms, or your screen’s brightness level in percent. All we know is that we’re dealing with a floating-point number with double precision (64 bits), but it doesn’t tell us what this number represents. For that reason, the semantic type-safety is lost.

Assume below example, there is a function we are about to pass two String param (username and password).

fun getAuthToken(username:String, password:String) {
....
}

see to the above function we are able to pass the param like this and which is wrong doesn't care at the compiler level. (even we have named params both accepts the String), Hence here primitives fails.

val uname = "juno ravi"
val pwd = "P@ssw0rd!123"

val token = getAuthToken(pwd, uname) 

This programming error cannot be detected at compile-time and will most likely lead to unexpected behavior. 💥

There are different approaches to solving the two problems mentioned above. We could just wrap a primitive type in a class, but that comes with a lot of overhead. So let’s see how we can tackle these problems above with

  • data classes, 😈
  • type aliases,  😜
  • and value classes 😎

Data classes: Instantiating data classes is expensive. Primitive values can be written to the stack which is fast and efficient. Instances of data classes are written to the heap, which takes more time and memory.

data class Username(val username: String)
data class Password(val password: String)

fun getAuthToken(username:Username, password:Password) {
....
}

with the above implementation is able to achieve but it is too costly process, comparatively if primitive took 2.x milliseconds means, it will take 8.x milliseconds for the single process.  Hence it is not advisable to do 😈

Type aliases: From the below Example, String & Username and Password are synonyms.
Whenever the compiler sees Username/Password, it basically replaces it with String and moves on.

typealias Username = String
typealias Password = String

Since our new Username/Password type alias is now equal to String, hence it works as same as primitvies which we have seen above, it also receives the same optimization treatment as Primitive but loses the typesafety. 😜

Value classes: It looks pretty similar to data classes. The signature looks exactly the same, except that instead of data class the keyword is value class, also there should be @JvmInline annotation to that class due to the Valhalla JVM release in future (currently JVM supports value class on the built in primitive types).  Important thing is the value class accepts only one constructor param.  

Since this value class acts as a wrapper to that variable, it also receives the same optimization treatment as Primitive which we can see it on the below screenshot on the highlighted section of the Kotlin byte code where as uname is referred it as Username class type, but pwd is referred as String value type, because it is defined with a value class 😎


Side Note: 

Since Kotlin 1.2.xx, we have inline classes, the old name for the value class. Since the class is not actually inlined in comparison to the inline function, it has been renamed to value class and the inline keyword is deprecated from Kotlin 1.5

Happy Coding :-)

Saturday, 26 February 2022

Get context in Jetpack Compose

In Android Compose, you can get the context by using the LocalContext, but it should be call'd from the composable function / scope.

val context = LocalContext.current

In the below code snippet we are retrieving the context and show a toast message inside the composable.

@Composable
fun MyToastDisplay(name: String) {
    val ctx = LocalContext.current
    Column(
        Modifier
            .fillMaxHeight()
            .fillMaxWidth(), verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(text = "Hello $name", color = Color.Red,
            modifier = Modifier
                .background(Color.Green)
                .clickable {
                    Toast
                        .makeText(ctx, "Welcome to the Compose World", Toast.LENGTH_SHORT)
                        .show()
                })
    }
}

If you use the LocalContext.current directly inside the clickable function results in the compilation error “@composable invocations can only happen from the context of an @composable function”.


Since the LocalContext.current is composable, you can’t invoke it within the non-composable function.  i.e. clickable function is not a composable function and so can’t accept other composable functions. 

Alternatively, you can get the context outside the clickable function scope and use, as shown in the above code snippet.


Happy Coding :-)

Sunday, 16 January 2022

Android FAB Action to appear across the Application

Recently we got a requirement to an existing product and which have some legacy implementation too. Here the requirement to have a FAB (Chat Bot) action to be available on all the application screens (Activity).

Discussion where went like below:

1. Adding FAB to all the activity is a very tedious process, even creating a common layout and add using <include> in all layout also not that simple task. 😟 (Bad Practice too)

2. Will create a simple FAB Action class and planned to initialize it in all the activity class, since it is also a required some more efforts to update the initialization code in all the activity classes. (This implementation will reduces the effort but not fully, It helps to update on our Common BaseActivity and few other independent Activities by doing the initialization)  😌   

3. Finally Achieved!..  Implemented through Application class and ActivityLifecycleCallbacks, which made the implementation easier to work seamlessly across the application. 😎

FabButton

object FabButton {
    fun init(activity: Activity, savedInstanceState: Bundle?=null) {
        val root: ViewGroup = activity.window.decorView as ViewGroup
        val fab = FloatingActionButton(activity).apply {
            id = View.generateViewId()
            setImageResource(android.R.drawable.sym_action_chat)
            elevation = 2f
            imageTintList = ColorStateList.valueOf(Color.WHITE)
            backgroundTintList = ColorStateList.valueOf(Color.RED)
            size = FloatingActionButton.SIZE_NORMAL
            isFocusable = true
            layoutParams = FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.WRAP_CONTENT,
                FrameLayout.LayoutParams.WRAP_CONTENT
            ).apply {
                gravity = Gravity.BOTTOM or Gravity.END
                setMargins(24, 24, 24, 24)
            }
        }
        fab.setOnClickListener {
            Toast.makeText(activity, "Yes, May I help you?", Toast.LENGTH_SHORT).show()
        }
        root.addView(fab)
    }
}

Activity

class FirstActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second2)
        FabButton.init(this)  // See here the fab initialization
    }
}

Later as per the #3, we found an other option instead of initializing the FabButton.init(this) to all the activities, implementing the below things resolved the issue without doing a larger code change.  

class TrailsApplication : Application(), Application.ActivityLifecycleCallbacks {
    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(this)
    }
    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        FabButton.init(activity, savedInstanceState)
    }
// Override other class ... 
}

P.S:  The application class has to override all the interface method of the ActivityLifecycleCallbacks, so as per the standard it would be good to handle by creating a separate handler class and register the same on application class.   For the simple and understanding purpose, I have override all the methods of lifecycle callbacks in application class itself. 

Happy Coding :-)