Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Sunday, 1 August 2021

Android Java to Kotlin migration by example part 13: operator overloading

This time again we're going to be talking about something available in Kotlin, but not in Java: operator overloading.

Operator overloading means that we can apply operators, like + (someobject+anotherone) or [ ] (someobject[5]) to our objects. Expression like this (someobject + anotherone) can be thought of as methods: it's just another way of expressing someobject.plus(anotherone). someobject[5] would mean "call the method [] with parameter 5 on someobject.  

Altough operator overloading is an old concept (it's common in C++, where - for example - the famous cout<<"Something"; actually uses overloaded left bit shift operator for object cout of class ostream; it's also available in C# and Python), it's not implemented in Java (which was a deliberate choice of its creators).

Still, it can be very useful and Kotlin allows it.

Let's get to our code. I think the most basic example of operator overloading is use of class representing complex numbers (the code below is the full program, with both class definition and it's use):

package com.mypackage

import kotlin.Exception
import kotlin.math.sqrt

class Complex(var re: Float, var im: Float){

fun modulus(): Float{ // just a method
return sqrt(re*re+im*im)
}

// "this" can be omitted
operator fun plus(another: Complex): Complex{ // a+b
return Complex(this.re+another.re,this.im+another.im)
}

operator fun minus(another: Complex): Complex{ // a - b
return Complex(re-another.re, im-another.im)
}

operator fun times(another: Complex): Complex{
var resultRe = re*another.re-im*another.im
var resultIm = re*another.im+im*another.re
return Complex(resultRe, resultIm)
}

operator fun compareTo(another: Complex): Int{ // == > < <= >=
if(this.modulus()<another.modulus())
return -1
if(this.modulus()>another.modulus())
return 1
return 0
}

operator fun get(arg: Int): Float{ // someComplex[0]
if(arg==0)
return this.re
if(arg==1)
return this.im
throw IllegalArgumentException()

}

override fun equals(other: Any?): Boolean {
if(other is Complex)
return (this.re==other.re && this.im == other.im)
else
throw Exception("wrong class")
}

override fun toString(): String{
return "($re,$im)"
}

}

fun main() {
val c = Complex(1.0f,0.0f)
val c2 = Complex(1.0f, 0.0f)
val c3 = Complex(10.0f,10.0f)
val sum = c+c2

val sum2 = c.plus(c2)

val someMul = Complex(10.0f,5.0f)* Complex(7.0f,-2.0f)

println("multiplied:$someMul")

println(sum)
println(sum2)
println(sum[0])
println(c3>c2)

println(c==c2)
}

... which defines the class Complex representing a complex number, and overloads the infix "+", "-", "*", comparison (<, >, >=, <=) and [ ] operators. There is also modulus function, which is just a method... And which we use to define our overloaded comparison operators behaviour (operator fun compareTo).

The execution of the code above results in the following output:

multiplied:(80.0,15.0)
(2.0,0.0)
(2.0,0.0)
2.0
true
true

We can see that my implementation of complex multiplication is correct ;)

The +, -, * operators, implemented as plus,  minus, and times are quite self-explanatory: they take one parameter, and use this.re and this.im (or just re and im) of current (left to operand) values to compute the results, which they return. 

In order to override them, we use operator keyword before a method definition. Please note they can be called both as operators ( val sum = c+c2 ) or just as methods ( val sum2 = c.plus(c2)).

In comparison operator overload (operator fun compareTo(another: Complex)) I've used previously defined modulus method, because there is no unequivocal way of comparing complex numbers - so I compare their moduli. This operator should return -1 if the first operand is "smaller" than second one (in our case it's modulus is smaller), 1 if it's "bigger" and and 0 it they're the same.

I've also overloaded get operator, which affects suqare brackets, like in the call println(sum[0]). What it does, is simply return real part of our complex as 0-th element and the imaginary part as 1-st one. If called with something else than 0 or 1, it would throw IllegalArgumentException(). Typically this operator would be used for some class representing collection (like list - please note in Java you'd have to use somelist.get(i) instead of just somelist[i] to get i-th element of the somelist List or like text string, where it would return n-th character).

There are other operators to overload, like not (!) - but I simply can think of no use of it regarding complex numbers :). When you type operator fun  (with the spacebar after the word fun) and hit Ctrl+Space in IntelliJ Idea or Android Studio, you'll get the full list of the operators possible to overload with their symbols on the right and corresponding keywords (like plus or minus in the example above) on the left:



Please note compareTo does not affect == comparison effect: that's what overriden function override fun equals(other: Any?) does.

A word on Java - of course, lack of operator overloading in Java doesn't mean we could not implement a class Complex in this language. We can, of course. But the only way of representing "sum of complex1 and complex2" would be some function or method, like complex1.add(complex2) or maybe sum(complex1, complex2) - not the intuitive and obvious Kotlin's complex1+complex2.

Well, that would be basics. More details, including the full list of operators available for overloading in Kotlin are available in the Kotlin's documentation.


Saturday, 31 July 2021

Android Java to Kotlin migration by example part 12: a few Kotlin features

Today I'm not going to use the format typical for this series, instead - I'll just show you two features of Kotlin I've just learned about. As there are no named arguments and default parameter values in Java, I cannot provide you the Java code counterpart this time :)

Kotlin functions have named arguments, actually exactly as in Python.

What that means (assuming you haven't read the previous post?). Well, let's consider the following code:

package com.mypackage

fun divide(nominator: Float, denominator: Float): Float{
return nominator/denominator
}
fun main() {

val result = divide(10.0f,5.0f)
val result2 = divide(nominator = 10.0f, denominator = 5.0f)
val result3 = divide (denominator = 5.0f, nominator = 10.0f)

println(result)
println(result2)
println(result3)
}

It produces the following result:

2.0 2.0 2.0

The function divide has (quite obviously) two arguments: nominator and denominator. If you call the function without specifying their names, their order matters: in our case, the first argument is divided by second one.

But... You can also call our function with named parameters: as in Python, here also it means you can switch their order (the calls initializing result2 and result3.

And second thing: also exactly as in Python, the parameters in Kotlin can have default values.

So that the following code, for example:

fun power(base: Float, exponent: Float = 2.0f): Float{
return base.pow(exponent)
}
fun main() {

val a2cubed = power(10f,3f)
var a2squared = power(10f)

println(a2cubed)
println(a2squared)
}

... makes power function actually a square function, when called with just one argument.

The code above produces the following result:

1000.0
100.0

I think that's all for today. Both named parameters and default values are neat features - especially in functional language - and another advantage of Kotlin over Java - it's good to know they're available.

Saturday, 17 July 2021

Android Java to Kotlin migration by example part 10: higher-order functions

 A higher-order function is a function, which returns another function, or takes another function as parameter.

Today we'll begin with Kotlin code. Kotlin, being much more functional-oriented than Java (although Java is getting more functional since version 8), makes use of higher-order function much more elegant and readable.

Let's consider a mathematical function A, mapping some value to other value, for instance y=x*x (square). We can construct another function B (or functionAMultipliedBy2) which would map any x to functionA(x)*2.

In functional programming we can use a higher-order function in that case: create a function functionB, taking functionA as param. Such function can then be stored like a variable, and called. I guess it's more understandable in the code form. So let us take a look at a program defining our functionMultipliedBy2, getting a Float->Float function (getting float as param and returning a float) as parameter and returning Float->Float function (the function passed as a parameter multiplied by 2). 

Kotlin:

package com.mypackage

fun functionMultipliedBy2(
functionArg: (arg: Float) -> Float): (Float) -> Float
{
val funcToReturn = {a: Float ->functionArg(a)*2}
return funcToReturn
}

fun squareA(arg: Float): Float{
return arg*arg
}

fun main() {
println(squareA(2.0f)) // "4.0"

// we can pass squareA as parameter to functionMultiplied by 2
val squareDoubled = functionMultipliedBy2(::squareA)
// :: because it's class member

// squareDoubled is now a FUNCTION, taking Float
// as param and returning 2* square(param)

// we can simply call it:
println(squareDoubled(2.0f)) // "8.0"
}

The same code in Java would look like this:

import java.util.function.Function;

import static java.lang.System.out;

public class MainClass {

static Function<Float, Float> functionMultipliedBy2(
Function<Float, Float> functionArg)
{
Function<Float, Float> funcToReturn =
input -> functionArg.apply(input) * 2;
return funcToReturn;
}

static float squareA(float arg){
return arg*arg;
}

public static void main(String[] args){
out.println(squareA(2.0f)); // "4.0"

Function<Float, Float> squareDoubled =
functionMultipliedBy2(MainClass::squareA);
// -> ugly

// squareDoubled is an object of type Function
// getting Float as param and returning 2*square (param)

// call looks like this:
out.println(squareDoubled.apply(2.0f)); // "8.0"

}
}

The code execution result is the same in both cases and it's:

4.0
8.0

Please note how the Java code makes use of class Function objects to deal with functions. In Kotlin, we define returned value (or acccepted as argument) as function simply by (Float) -> Float notation.

What's more, in Kotlin we can simply call passed function. A Java Function object has to be used by calling .apply method to it (because it's an object, not a "real" function). I think Kotlin, as more functional-oriented languages, deals with this in much more "natural" way.

The higher-order function can also be used with lambda expressions. In Java:

import java.util.function.Function;

import static java.lang.System.out;

public class MainClass {

static Function<Float, Float> functionMultipliedBy2(
Function<Float, Float> functionArg)
{
Function<Float, Float> funcToReturn =
input -> functionArg.apply(input) * 2;
return funcToReturn;
}

public static void main(String[] args){
Function<Float, Float> squareRoot =
input -> (float)Math.sqrt((double)input);
Function<Float, Float> squareRootDoubled =
functionMultipliedBy2(squareRoot);
out.println(squareRootDoubled.apply(4.0f));
}
}

... as well as in Kotlin:

package com.mypackage

import kotlin.math.sqrt

fun functionMultipliedBy2(
functionArg: (arg: Float) -> Float): (Float) -> Float
{
val funcToReturn = {a: Float ->functionArg(a)*2}
return funcToReturn
}

fun main() {
val squareRoot = {arg:Float -> sqrt(arg.toDouble()).toFloat()}
val squareRootDoubled = functionMultipliedBy2(squareRoot)
println(squareRootDoubled(4.0f))
}

Again, Kotlin is much more concise - and allows us to simply call the function, without using any special .apply() methods.

Friday, 16 July 2021

Android Java to Kotlin migration by example part 9: extension functions

One of Kotlin features which are not available in Java are extension functions. These are the functions which can be used as methods with existing class objects, but are not defined in them. You may already know them if you work with C# - they've been there for some time now.

It's quite useful, when we want to, for instance, define a method we would like to use with String type, but it's not defined by default. Of course, we could inherit from String, but it would mean we have to use the subclass everywhere when we could possibly want to use out new method.

Other solution is to simply create a function, getting our String as param. This is the easiest way this could be done in languages without extension functions (like Java). Let's consider the following function, counting uppercase characters in a string given as its param and returning the result:

Listing 1 (Java)

import static java.lang.System.out;

public class MainClass {

static int howManyUppercase(String string){
int res =0;
for(int i=0;i<string.length();i++)
if(Character.isUpperCase(string.charAt(i)))
res++;
return res;
}

public static void main(String[] args){
String string ="SoMe WoRdS";
out.println(howManyUppercase(string));
}
}

In Kotlin we could write very similar code. But we can also do this:

Listing 2 (Kotlin)

package com.mypackage

fun String.howManyUppercase(): Int{
var res = 0
for(i in 0..this.length-1)
if(this[i].isUpperCase())
res++
return res
}

fun main() {
var string = "SoMe WoRdS"
println(string.howManyUppercase())
}

We define our extension function as we would declare a normal function, but its name is preceded with extended class (in our case that's String ) and a dot.

Our howManyUppercase() becomes basically a String method. It's called like one, and is suggested by the IDE autocompletion.

It's important to remember that extensions are available only in the package they're defined (which makes sense, as it prevents possible conflicts), so it you want to use it outside of that package, the extension (or all the package contents) has to be imported, as shown below (the version with whole package import is commented out):

Listing 3 (Kotlin import)

package com.mypackage2 //!

import com.mypackage.howManyUppercase
// import com.mypackage.* // alternative

class AnotherClass {
fun myStringSomething(): Int{
return "blabla".howManyUppercase()
}
}

The extension functions can "extend" any class - although it seem to make more sense to use them with the standard defined types (as String), which source is not directly available (they're already compiled), you can also extend your own classes.

Please note that as extension function can extend any class, they can also extend Any class :)

In that case, the function can be called for basically every object in the scope of extension function:

package com.mypackage

import java.util.*

fun Any.howManyUppercase(): Int{
if(this is String) {
var res = 0
for(i in 0..this.length-1)
if(this[i].isUpperCase())
res++
return res
}
else
return(-1)
}

fun main() {
var string = "SoMe WoRdS"
println(string.howManyUppercase())
var someDate = Date()
println(someDate.howManyUppercase())
var num: Int = 2
println(num.howManyUppercase())
}

This would return -1 for the objects which are not String. We're using is operator and smart casting, mentioned in the previous post. And although the example above seem to not have much sense (what's the point of extending Any, if de facto only extended type is String?), we can consider the following case:

package com.mypackage

import java.util.*


fun Any.howManyUppercase(): Int{
when (this) {
is String -> {
var res = 0
for(i in 0..this.length-1)
if(this[i].isUpperCase())
res++
return res
}
is CharArray -> {
var res = 0
for(i in 0..this.size-1)
if(this[i].isUpperCase())
res++
return res
}
else -> return(-1)
}
}

fun main() {
var string = "SoMe WoRdS"
println(string.howManyUppercase())
var someDate = Date()
println(someDate.howManyUppercase())
var num: Int = 2
println(num.howManyUppercase())

var arr = string.toCharArray()
println(arr.howManyUppercase())
}

Now if we apply our extension to String or CharArray, it returns what we want. In any other case, it returns -1. If we had some more complex task, it's possible we would like to extend more types this way (using only one method).

Thursday, 15 July 2021

Android Java to Kotlin migration by example part 8: type checks and smart cast

Let's consider the following Java code:

import static java.lang.System.out;

public class MainClass {

static void printPiTimesValue(Object value){
if(value instanceof Float)
out.println(3.14f*(Float)value);
if(value instanceof Double)
out.println(3.14*(Double)value);
if(value instanceof String)
out.println(3.14*Double.parseDouble((String)value));
}

public static void main(String[] args){
printPiTimesValue(2.0);
printPiTimesValue(2.0f);
printPiTimesValue("2.0");
}
}
(I've imported System.out, to write out.println instead of System.out.println every time).

So our printPiTimesValue gets something as param (Object, which in Java can be basically anything), and checks its type with the instanceof operator. If the param is float or double, the method casts it, multiplies by 3.14 and prints value. If it's String, the method casts it to String, then parses Double and prints the multiplied value again.

The program execution results in following output:

6.28
6.28
6.28

Now let's look at the Kotlin couterpart of our code:

package com.mypackage

fun printPiTimesValue(value: Any){
if(value is Float)
println(3.14f*value)
if(value is Double)
println(3.14*value)
if(value is String)
println(3.14*value.toDouble())
}


fun main() {
printPiTimesValue(2.0)
printPiTimesValue(2.0f)
printPiTimesValue("2.0")
}
It works the same way and gives the same results. We use Any in place of Object. Instead of Java instanceof operator, we use Kotlin is, which seems to do the same (return true if the left operand is the type specified by right operand).
Other than that, it looks similar, right?

Well, not exactly.
Where are the casts?
In Java, we had a cast everytime we wanted to use our Object as, for instance, Float, we had to:
1) check its type with instanceof
2) cast it.

In Kotlin - if the type check with  is succeeds, the value is automatically casted using a mechanism called smart cast!
The only case where we actually had change the type is the third if, where the smart cast already casted our value to String, but we still have to parse it as Double (as we had to in Java).


Wednesday, 14 July 2021

Android Java to Kotlin migration by example part 7: companion object problem

 In the previous part we've seen how to use companion objects in Kotlin, giving us something similar to Java/C# static fields and methods.

But there is a problem with it, when using inheritance: companion objects are not accessible from the subclass.

To understand what I mean, look at this Java code:

Listing 1 (Java)

class SuperClass{
static int field = 100;

public static void classMethod(){
System.out.println(
"superclass static method called, field = "
+ field);
}
}
class SubClass extends SuperClass{

public static void subclassMethod(){
field--;
System.out.println("Field value decremented: "
+ field);
}
}

public class MainClass {
public static void main(String[] args){
SuperClass superObject = new SuperClass();
SuperClass.classMethod();

SubClass subObject = new SubClass();
SubClass.subclassMethod();
SubClass.classMethod();
}
}

Code execution result is:

Listing 2 (result)

superclass static method called, field = 100
Field value decremented: 99
superclass static method called, field = 99

It has a superclass, a subclass, both with one static method, the superclass with one static field.

In main method we can instantiate them, and:

- for the superclass instance we can call the superclass static method.

- for the subclass instance we can call both the superclass and the subclass static method.

This makes sense - the subclass instance is the superclass instance. This is how inherintance works, right?

So take a look at this Kotlin code:

Listing 3 (Kotlin)

package com.mypackage


open class SuperClassWithCompanion{
companion object CompanionObj{
var field: Int = 100;
fun classMethod(){
print("superclass companion method called, field = "
+field)
}
}
}
class SubClass: SuperClassWithCompanion(){
companion object CompanionObj {
fun subclassMethod() {
field--;
println("Field value decremented: "
+ field)
}
}
}

fun main() {

val superObject = SuperClassWithCompanion()
SuperClassWithCompanion.classMethod()

val subObject = SubClass()
SubClass.subclassMethod()
//SubClass.classMethod()
}

It's basically the same as the code in Listing 1, we're just using companion objects instead of static fields and methods. So we create a superclass instance, call its static method, the subclass instance, call it's static method (subclassMethod()) and then...

We can't call the classMethod from the subclass. It won't compile, giving the Unresolved reference: classMethod error.

And here is the problem: the companion object is not inheritable. We could say accompanies a particular class, not it's subclass.

The problem, although can be surprising; is - in fact - is not very complicated to solve. As the methods and fields of the companion object are common to all instances, we can just call SuperClassWithCompanion.classMethod() whenever we want to call SubClass.classMethod(). What's more, we can simply add such method (the same name and prototype) in the subclass and call the superclass method there:

Listing 4 (Kotlin)

package com.mypackage


open class SuperClassWithCompanion{
companion object CompanionObj{
var field: Int = 100;
fun classMethod(){
println("superclass companion method called, field = "
+field)
}
}
}
class SubClass: SuperClassWithCompanion(){
companion object CompanionObj {
fun subclassMethod() {
field--;
println("Field value decremented: "
+ field)
}
fun classMethod(){
SuperClassWithCompanion.classMethod()
}
}
}

fun main() {

val superObject = SuperClassWithCompanion()
SuperClassWithCompanion.classMethod()

val subObject = SubClass()
SubClass.subclassMethod()
SubClass.classMethod()
}

Then it works the same as our code from Listing 1:

Listing 5 (result)

superclass static method called, field = 100
Field value decremented: 99
superclass static method called, field = 99

Still, there seems to be some redundancy here, as we have to "manually" call the companion object's methods in the subclass.

The question is - does it make sense? Well, sort of. Tke Kotlin designers purposefully made it impossible to inherit the companion object: because mixing inheritance and static code in Java often led to confusion and errors.

As far as I can see, it doesn't prevent us from doing what we want - maybe just forcing to do it in more explicit way.

 

Friday, 9 July 2021

Android Java to Kotlin migration by example part 4: Data classes

 Today we'll see a Kotlin feature, which is not available in Java: data classes.

Let's consider a following Java class:

Listing 1 (Java)

class Human{
String name;
int age;

public Human(String name, int age){
this.name =name;
this.age = age;
}

@Override
public String toString() {
return "Human(" +
"name=" + name +
", age=" + age +
')';
}
}

We could "use" it this way:

Listing 2 (Java)

public class Main{
public static void main(String []args){
Human john = new Human("John Watson", 30);
john.age = 31;
System.out.println(john);
}
}

with the following result:

Listing 3 (result)

Human(name=John Watson, age=31)

Now, this class is kind of class that in Kotlin would be known as data class. Which means it doesn't provide any method, just fields (and one constructor, allowing to initialize them).

And the code analogous to Listing 1 would be:

Listing 4 (Kotlin)

data class Human(var name: String, var age: Int)

... that's right! That's all, one line. The data modifier results in generating a class having all the fields and the default parametered constructor, as well as some other methods, including toString() implementation, printing . The main method in Kotlin would be as follows:

Listing 5 (Kotlin)

fun main(args: Array<String>) {
var john = Human("John Watson", 30)
john.age = 31
print(john)
}

.. And that's all 24 lines of Java code become 6 of Kotlin!

What more can we do with data classes? Well, we can define the default (parameterless) constructor, like this code in Java:

Listing 6 (Java)

package com.company;

class Human{
String name;
int age;

public Human(String name, int age){
this.name =name;
this.age = age;
}

public Human(){
this.name = "Sherlock Holmes";
this.age = 29;
}

@Override
public String toString() {
return "Human(" +
"name=" + name +
", age=" + age +
')';
}
}

public class Main{
public static void main(String []args){
Human john = new Human("John Watson", 30);
System.out.println(john);
Human sherlock = new Human();
System.out.println(sherlock);
}
}

...making the "default" human Sherlock Holmes :) 

Listing 7 (result)

Human(name=John Watson, age=30)
Human(name=Sherlock Holmes, age=29)

The only modification in our Kotlin code from Listning 4 would be adding these default values to our data class fields. So the whole program becomes:

Listing 8 (Kotlin)

package com.mypackage

data class Human(var name: String = "Sherlock Holmes", var age: Int = 29)

fun main(args: Array<String>) {
var john = Human("John Watson", 30)
println(john)
var sherlock = Human()
println(sherlock)
}

So over 20 lines of Java code (the Human class definition) became 1 in Kotlin!

Although data classes are quite specific (well, usually classes have some methods more sophisticated than toString() ), if such a simple class is needed - Kotlin code can become extremely short and powerful.

Sunday, 4 July 2021

Android Java to Kotlin migration by example, part 1

Well, I've recently started to catch up with Kotlin programming language, which replaces Java as main Android development language.

This post will be a first in the series - in this one I'll simply "translate" some Java code to Kotlin, in future ones I'll elaborate more on Kotlin's syntax (making them something like tutorial, I hope).

I've created a simple (one activity) app - in Java at first - which takes the text from an EditText, saves it to internal data, allows to read it and allows to add it as a note to the notification area. There are also some unnecessary (in this context) things, like a constant - it will be our excuse to show some Kotlin's syntax.

In the beginning, let's create a simple layout:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="160dp"
android:layout_marginBottom="32dp"
android:onClick="onAddNotificationButtonClick"
android:text="Add notification"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.486"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.171" />

<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="392dp"
android:onClick="onSaveClick"
android:text="Save"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.281"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="392dp"
android:onClick="onLoadClick"
android:text="Load"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.758"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="76dp"
android:onClick="onButtonOpenClick"
android:text="Open"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />

<EditText
android:id="@+id/editTextTextMultiLine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="90dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="111dp"
android:ems="10"
android:gravity="start|top"
android:inputType="textMultiLine"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button4" />

</androidx.constraintlayout.widget.ConstraintLayout>

... and save it as activity_main.xml in the res/layout .

In layout editor it looks like this:


It has a button, which adds a notification to the tray, then a multiline edit-text allowing us to input the notification's content, a textView which I forgot to use ;), and then three buttons:

- the Save button causes textView content to be saved in internal storage (persistantly)

- the Load button reads data from internal storage and shows them in the textView

- the Open button opens another Activity... I'll exlain in a moment.

Let's begin with creating the typical Android Activity class and initializing some textView and multiline edit text. To be specific - I'll create two separate activities, but they're be exactly the same (even using the same layout). One will be in Java, another one - in Kotlin. The Open button in each of them will open the other one: Java -> Kotlin -> Java...

JAVA:

public class MainActivity extends AppCompatActivity {

TextView tv;
EditText editTextTextMultiline;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = findViewById(R.id.textView);
editTextTextMultiline = findViewById(R.id.editTextTextMultiLine);
}

KOTLIN:

class SecondActivity : AppCompatActivity() {

// the default visibility modifier is public:
private lateinit var tv: TextView;
private lateinit var editTextMultiline: EditText;

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tv = findViewById(R.id.textView);
editTextMultiline = findViewById(R.id.editTextTextMultiLine)
}

Ok, so let's go:

- in Kotlin, the default visibility modifier is public (package-private in Java). A bit strange if you're used to most of the object-oriented languages, but doesn't seem to be a problem. BTW Android Studio marks the functions it 'thinks' could be private and allows us to change the visibility automatically.

- the variable declaration is done using the keyword var.

- you can't just declare variable without initialing it OR stating explicitly it's to be initialized later by using the lateinit keyword.

- the type declaration syntax is bit funny, looks Pascal-like to me. 

- override becomes modifier instead of annotation

- the nullable parameter has to be expliitly stated as nullable (Bundle?, not Bundle type)

- semicolons after instructions are optional (unnecessary, in fact, I'll try to avoid them later)

- function declaration looks different (more about this in a moment)

Ok, now let's declare a variable, a constant, a simple function and a function called by the button in activity (chosen in a layout xml with onClick):

Java:

int someNumber = 0;

final float PI = 3.1415f;

int getIncrementedNumber(int number){
return number+1;
}
public void onChangeTextClick(View v) {
someNumber = getIncrementedNumber(someNumber);
tv.setText(String.format(Locale.getDefault(), "%d", someNumber));
}

Kotlin:

var someNumber = 0;

val PI = 3.1415f;

private fun getIncrementedNumber(number: Int): Int{
return number+1
}

fun onChangeTextClick(v: View) {
someNumber = getIncrementedNumber(someNumber)
tv.text = String.format(Locale.getDefault(), "%d", someNumber)
}

- constant is declared using val keyword

- function is declared using fun keyword, the returned type for function is added after as a last part of its prototype (: Int{ part). Analogous to variable declaration. If function does not return anythong (void-like), the returned type part is omitted.

- getters and setters are refered to like field names, not like methods (no parenthesis).

Overall, code in Kotlin seems to be a bit more concise.

Now let's do the most basic thing in Android: open another activity. The Kotlin one will open Java activity, the Java activity will open Kotlin one :)

Java:

public void onButtonOpenClick(View v) {
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
}

Kotlin:

fun onButtonOpenClick(v: View) {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}

In Kotlin:

- using val instead of explicit class name save us some space

- constructor is called without the new keyword

- reference to class looks a bit different. 

Now something longer. Let's create a method for creating a notification channel. It will work just on Android 8.0+ for now (the system changed then):

Java:

private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "chanName";
String description = "chanDesc";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(
"someChannelId432", name, importance);
channel.setDescription(description);

NotificationManager notificationManager =
getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}

Kotlin:

private fun createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = "chanName"
val description = "chanDesc"
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(
"someChannelId4234", name, importance)
channel.description = description

val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}

Creating notificationManager requires casting - to be honest, I don't know why.

Method for saving some string to file (internal storage):

Java:

void saveDataToFile(String filename, String fileContents) {
try {
FileOutputStream fos =
this.getBaseContext().openFileOutput(filename,
Context.MODE_PRIVATE);
fos.write(fileContents.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}

Kotlin:

private fun saveDataToFile(fileName: String, fileContents: String) {
try {
val fos: FileOutputStream =
this.baseContext.openFileOutput(fileName, Context.MODE_PRIVATE)
fos.write(fileContents.toByteArray())
} catch (e: IOException) {
e.printStackTrace()
}
}

(please mind the exception type declaration syntax in catch) 

... and for reading from file:

Java:

String readFromFile(String filename) {
FileInputStream fis;
StringBuilder stringBuilder = new StringBuilder();
try {
fis = MainActivity.this.openFileInput(filename);

InputStreamReader isr =
new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr);
String line = reader.readLine();
while (line != null && line!="") {
stringBuilder.append(line);
line = reader.readLine();
if(line!=null)
stringBuilder.append('\n');
}
} catch (IOException e) {
// Error occurred when opening raw file for reading.
}
return stringBuilder.toString();
}

Kotlin:

private fun readFromFile(fileName: String): String {
val fis: FileInputStream?
val stringBuilder = StringBuilder()
try {
fis = (this@SecondActivity).openFileInput(fileName)

val isr = InputStreamReader(fis, StandardCharsets.UTF_8)
val reader = BufferedReader(isr)
var line = reader.readLine()
while (line != null && line != "") {
stringBuilder.append(line)
line = reader.readLine()
if(line!=null)
stringBuilder.append('\n')
}
} catch (e: IOException) {
// Error occurred when opening raw file for reading.
}
return stringBuilder.toString()
}

FileInputStream? has to explicitly declared with ? as nullable. if, while and return statements are similar to Java.

Now let's call our methods:

public void onSaveClick(View v) {
saveDataToFile(someFileName, editTextTextMultiline.
getText().toString());
}

public void onLoadClick(View v) {
editTextTextMultiline.
setText(readFromFile(someFileName));
}

becomes:

fun onSaveClick(v: View) {
saveDataToFile(someFileName, editTextMultiline.text.toString())
}

fun onLoadClick(v: View) {
editTextMultiline.setText(readFromFile(someFileName))
}

Finally, let's create the method for adding notification, called when "Add notification" button is pressed.

Java:

    public void onAddNotificationButtonClick(View v) {

createNotificationChannel();
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this,
"someChannelId432")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("My notification")
.setContentText("")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(editTextTextMultiline.getText()))
.setPriority(NotificationCompat.PRIORITY_DEFAULT);

NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(this);
notificationManager.notify(4234, builder.build());
}
} // class end

...and Kotlin:

    fun onAddNotificationButtonClick(v: View) {

createNotificationChannel()
val builder = NotificationCompat.Builder(this,
"someChannelId432")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("My kotlinofication")
.setContentText("")
.setStyle(NotificationCompat.BigTextStyle().
bigText(editTextMultiline.text))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)

val notificationManager = NotificationManagerCompat.from(this)
notificationManager.notify(3213, builder.build())
}
} // class end

Again: it's similar. Lack of explicit types declarations (NotificationCompat.Builder builder becomes val builder) no new keyword and lack of semicolons makes the code a bit shorter. Quite readable, in my opinion.

One last thing: manifest, without it updated the second activity couldn't be open. It should look like this:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplicationjava">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplicationJava">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity">
</activity>
</application>

</manifest>

That's it for now. In the future posts I'll elaborate more on details, providing more examples of short Java code and corresponsing Kotlin code.

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...