Android Application Development All-in-One For Dummies
Book image
Explore Book Buy On Amazon
There aren’t any perfect programming languages. However, Google has decided that Kotlin is as close as it gets when it comes to developing Android apps. Having said that, Java lovers can still use their favorite language to make their Android apps.

Part of the problem is that no one can agree on what constitutes perfect. People like to fight about things, including programming language features. With this in mind, you might feel sorry for Java as the victim of mean-spirited finger pointing by those who just don’t like the language.

However, as you’ll soon discover, Java has some real issues that Kotlin attempts to fix. Whether you agree with the fixes is a matter for you to decide, of course.

Kotlin improves control over null references

A null reference describes an object that points to nothing. It’s sort of like having a mailbox on a post outside of an empty lot. Yep, there is a mailbox there, but no one will ever get the mail. The mailbox, the object, seems to point to something, but when you investigate it, you find that it doesn’t point to anything at all —the lot is empty.

When you try to access an object that points to null in Java, you get the dreaded NullPointerException. Of course, the first thing on your mind is determining what happened to the house that’s supposed to appear on that empty lot.

Unfortunately, we’ve been dealing with the null reference since 1965, when Tony Hoare decided to add it to ALGOL W. He calls it his billion-dollar mistake during the 2009 QCon London presentation. When the inventor of a method of doing something says it was a horrid mistake, getting rid of it seems like a good idea. But getting rid of the null reference would mean breaking a lot of Java code, so that’s where Kotlin comes into play.

Kotlin doesn’t actually remove null references entirely, but it does help you control them better. For example, the following code results in a compilation error in Kotlin:

var a: String = "abc"
a = null
println(a)
A String is a non-nullable type. The type system ensures that you can’t place a null value in a for any reason. Consequently, a can never experience a NullPointerException.

Of course, there must be allowances. You can tell Kotlin to allow null values in specific cases by using a special operator. The following code defines a nullable String and will compile:

var a: String? = "abc"
a = null
println(a)
However, when you print a, what you see is the value null, not a NullPointerException. Consequently, the null value is still safe. However, it comes with limits. For example, when using the following code, you get an output value of 3.
val a: String = "abc"
println(a.length)
The limitation comes when you make String nullable. The following code produces a compilation error saying that only safe calls are allowed on nullable String objects.
val a: String? = "abc"
println(a.length)
You still might want to check the length of a. Kotlin has a workaround for this issue as well. The following code first determines whether a is null and, if it isn’t, returns length:
val a: String? = "abc"
println(a?.length)
What if you want to assign a potentially null value to another variable? In this case, you rely on the Elvis operator (?:). The following code safely assigns that length of a to b, unless a is null, in which case, b receives a value of 0:
val a: String? = "abc"
val b = a?.length ?: 0
println(b)
It seems that Kotlin is quite determined not to allow you to produce a NullPointerException in your code. If you’re equally determined to produce one, you can use the not-null assertion operator (!!), as shown in the following code:
val a: String? = null
println(a!!.length)
Kotlin goes only so far to protect you from yourself. You can still see a NullPointerException in these situations:
  • The throw NullPointerException() being specifically called by the code.
  • The use of the non-null assertion operator (!!), described earlier.
  • A data inconsistency that results from improper initialization:
  • Your code calling Java code that produces a NullPointerException. Unfortunately, Kotlin can’t protect you from Java.

Kotlin removes raw data types

A raw data type occurs when you create a generic Java type without specifying a data type as part of the declaration. For example, here is a raw data type in Java:
List names = Arrays.asList("John", "Mia", "Herb", "Ann");
This code will compile in Java. The problem with this code is that you have no idea what data type names contains; it could be anything. More important, the compiler now has to guess what data type to use, and forcing the compiler to guess is always a bad idea. Plus, to use the values in names, you must now cast the individual entries, such as
Iterator iter = stars.iterator();
while(iter.hasNext()) {
  String name = (String) iter.next(); //cast needed
  println(name);
}
Kotlin can use Java types. In fact, you can interoperate with Java successfully using certain techniques. However, to use names in Kotlin, you must specifically declare a type, as shown here:
val names: List = 
   Arrays.asList("John", "Mia", "Herb", "Ann")

for (name in names){ println(name) }

Notice that using Java types in Kotlin is a little different from how you use them in Java. However, the basic principle is the same. You define names as type List<String> and then assign it names using Arrays.asList("John", "Mia", "Herb", "Ann"). The point is that you can’t use raw data types because the compiler will display an error.

Kotlin uses invariant arrays

When working with Java, you can copy an array of a specific type, such as String, to an array of type Object. You can then manipulate the items in the Object array as desired. Consequently, code like this will compile without problem in Java:
String[] stuff = {"Tom", "Sam", "Ann"};
Object[] moreStuff = stuff;
You can do things this way because arrays are covariant in Java. String is subclassed from Object, so an Object can contain a String. However, when you’re able to do things like this, you can encounter problems. Consider the following code:
String a = stuff[0];
System.out.println(a);
String b = moreStuff[0];
System.out.println(b);
This code won’t compile because Java loses track of the type of the data in moreStuff. It now views element 0 as an Object, not a String, so it can’t make the second assignment to b. Of course, you can overcome this issue by using a cast: String b = (String)moreStuff[0];. However, now that you’ve fixed one problem, you have another. Look at this code:
String a = stuff[0];
System.out.println(a);
moreStuff[0] = 5;
String b = (String)moreStuff[0];
System.out.println(b);</pre<
You now get a runtime error because you can’t cast an Integer (the changed type of moreStuff[0]) to a String. Java overcomes this problem by using generics, which are invariant (objects must be of the same type when you set one equal to the other). This code won’t compile because converting a String to an Object isn’t possible.
List<String> stuff = Arrays.asList("Tom", "Sam", "Ann");
List<Object> moreStuff = stuff;
Generics are invariant in Java. Likewise, arrays are invariant in Kotlin. Consequently, this Kotlin code won’t compile for the same reason that generics won’t compile in Java:
val stuff: Array<String> = arrayOf("Tom", "Sam", "Ann")
val moreStuff: Array<Any> = stuff

Kotlin lets you work with proper function types

The term proper function types refers to the kind of programming performed using lambda functions in a functional programming way. Most Java developers aren’t really familiar with functional programming because functional programming is historically difficult to implement in Java, and addressing all the tenets of functional programming isn’t possible.

However, Java developers can perform functional programming after a fashion by using techniques such as Single Abstract Method (SAM) conversions. To give you some idea of how functional programming differs from Java programming, here are the basic tenets:

  • Creating functions that are first-class objects
  • Using pure functions
  • Employing higher-order functions
  • Relying on a lack of function state
  • Depending on an absence of side effects
  • Using immutable variables
  • Favoring recursion over looping
As you can see, trying to implement most of these requirements in Java would be impossible, or at least nearly so. However, this article demonstrates that some of them are possible and that there are distinct benefits to using this functional programming approach. For example, here’s an example of a Java pure function:
public class performSum{

public int sum(int a, int b) { return a + b; } }

The function doesn’t have state, it doesn’t use any sort of member variables, and it relies only on the input variables. Because of this, you can be sure that every call to this function with a given set of inputs will have precisely the same result every time.

Java’s lambda functionality relies on SAM, which is an interface version (also called a functional interface) of the pure function. Here’s an example of SAM:

public interface OnClickListener {
    void onClick(View v);
}
A SAM conversion occurs when Java converts the SAM interface into functional code. Here is the lambda expression for the SAM interface:
myView.setOnClickListener((view) -> logger.debug(
    "View Clicked!"));
Unlike Java, Kotlin provides first-class support for lambda functions. You don’t have to create an interface specification in Kotlin to achieve the desired result — all you need is a signature specification provided as part of your function. Consequently, the Kotlin version of the Java example looks like this:
fun setOnClickListener(listener: (view: View) -> Unit) {
   // ...
}
Notice that the listener is defined as part of the function signature. The signature defines the callable parameters and the resulting output. More important, a callable parameter can be just about anything, including a function. This is an extremely quick overview of a more complex process.

The main issue to consider when working with the Kotlin form of lambda expressions is that the Kotlin form is essentially incompatible with the Java form, making interop a lot more difficult to deal with. You can use the Java form of lambda expressions in Kotlin when working with Java code. However, when working with Kotlin code, you must use the Kotlin form instead, which means that you now have to know two ways of making lambda expressions work.

Kotlin gets rid of the checked exceptions

Checked exceptions may have seemed like a good idea at first because the developer is forced to ensure that code using particular methods also checks for exceptions that can occur as a result of relying on that method. Over the years, however, developers have found that checked exceptions don’t necessarily create robust code and that they do waste of a lot of time. Here are some resources to consider: The point of these articles is that checked exceptions were an experiment that didn’t work. Languages that follow the Java example don’t use them for a good reason. When you look at C# and Ruby, you don’t see checked exceptions, so it’s hardly a surprise that Kotlin eliminates them, too.

Want to learn more? Check out this article discussing Kotlin.

About This Article

This article is from the book:

About the book authors:

Dr. Barry Burd holds an M.S. in Computer Science from Rutgers University and a Ph.D. in Mathematics from the University of Illinois. Barry is also the author of Beginning Programming with Java For Dummies, Java for Android For Dummies, and Flutter For Dummies.

John Mueller has produced 114 books and more than 600 articles on topics ranging from functional programming techniques to working with Amazon Web Services (AWS). Luca Massaron, a Google Developer Expert (GDE),??interprets big data and transforms it into smart data through simple and effective data mining and machine learning techniques.

This article can be found in the category: