Home » Android » java – Why Kotlin doesn't have explicit typing?

java – Why Kotlin doesn't have explicit typing?

Posted by: admin June 15, 2020 Leave a comment

Questions:

I am curious about this, why would kotlin designers think this would be a good idea to drop explicit typing in Kotlin ?

To me, explicit typing is not a “pain” to write in Java (or any other strongly typed language) : all IDE can assist me to automaticaly type my variable.

It adds great understanding of the code (that’s why I don’t like weakly-typed languages, I have no idea what kind of variables I’m dealing with).

Also, and this is my main issue with that, it makes the code more bug-prone.

Example :
Java : Easily identified as a String, all good

String myPrice = someRepository.getPrice(); // Returns a String
myTextView.setText(myPrice); 

Java : Easily identified as an int, code smell with setText()

int myPrice = someRepository.getPrice(); // Returns an int, code smell !!
myTextView.setText(String.valueOf(myPrice)); // Problem avoided

Kotlin : ????

val myPrice = someRepository.getPrice(); // What does it return ?
myTextView.setText(myPrice); // Possible hidden bug with setText(@StringRes int) instead of setText(String) !!

No explicit typing in Kotlin is the biggest drawback in Kotlin imo. I try to understand this design choice.

I’m not really looking for “patches” to fix the example / avoid the presented code smell, I try to understand the main reason behind the removal of explicit typing. It has to be more than “less typing / easier to read”. It only removes a couple of characters (one still have to write val / var), and explicit typing in Kotlin adds some caracters anyway…

Static typing without explicit typing is, to me, the worst case scenario to deal with hidden bugs / spaghetti bugs : if one class (let’s say “Repository”) changes it return type (from String to int for example). With explicit typing, compilation would fail at the class calling “Repository”. Without explicit typing, compilation may not fail and the wrong type of variable may “travel” through classes and change the behavior of the classes because of its type. This is dangerous and undetected.

The fix is easy ; explicitly type the variable. But this is Kotlin we’re speaking, a language made for code golfers : people won’t explicitely type their variables, as it takes even more time to do so in kotlin than turtle-java. Yikes!

How to&Answers:

First of all: Kotlin has static typing. The compiler knows exactly which type goes or comes where. And you are always free to write that down, like

fun sum(a: Int, b: Int): Int {
  val c: Int = 1

The point is: you can write a lot of code that simply relies on the fact that anything you do is statically typed, and type checked, too!

Do you really need to know whether you are adding two doubles or two ints or two longs, when all you “care” to look at is a + b?

In the end, this is about balancing different requirements. Leaving out the type can be helpful: less code that needs to be read and understood by human readers.

Of course: if you write code so that people constantly turn to their IDE for the IDE to tell them the actual, inferred type of something, then you turned a helpful feature into a problem!

The real answer here is: it depends. I have been in many code reviews where C++ people discussed the use of the auto keyword. There were a lot of situations, where using auto did make the code much easier to read and understand, because you could focus on “what happens with the variable”, instead of looking 1 minute at its declaration, trying to understand its type. But there were also occasional examples where auto achieved the exact opposite, and a “fully typed” declaration was easier to follow.

And given the comment by the OP: you should know what you are doing in the first place. Meaning: when you write code, you don’t just invoke “any” method that you find on some object. You call a method because you have to. You better know what it does, and what it returns to you. I agree, when someone is in a rush, you quickly var-assign and then pass that thing along, that might lead to errors. But for each situation where var helps with creating a bug, there might be 10 incidents where it helps writing easier to read code. As said: life is about balancing.

Finally: languages shouldn’t add features for no reason. And the Kotlin people are carefully balancing features they add. They didn’t add type inference because C++ has it. They added it because they carefully researched other languages and found it to be useful to be part of the language. Any language feature can be misused. It is always up to the programmer to write code that is easy to read and understand. And when your methods have unclear signatures, so “reading” their names alone doesn’t tell you what is gong on, then blame that on the method name, not on type inference!

Answer:

To quote Java’s Local-Variable Type Inference JEP:

In a call chain like:

int maxWeight = blocks.stream()
                    .filter(b -> b.getColor() == BLUE)
                    .mapToInt(Block::getWeight)
                    .max();

no one is bothered (or even notices) that the intermediate types Stream<Block> and IntStream, as well as the type of the lambda formal b, do not appear explicitly in the source code.

Are you bothered about it?

if one class (let’s say “Repository”) changes it return type (from String to int for example). With explicit typing, compilation would fail at the class calling “Repository”.

If you have overloads like setText in your example, then

Repository repository = ...;
myTextView.setText(repository.getFormerlyStringNowInt());

won’t fail without type inference either. To make it fail, your code standard has to require every operation’s result to be assigned to a local variable, as in

Stream<Block> stream1 = blocks.stream();
Predicate<Block> pred = b -> { 
    Color c = b.getColor();
    return c == BLUE;
};
Stream<Block> stream2 = stream1.filter(pred);
ToIntFunction<Block> getWeight = Block::getWeight;
IntStream stream3 = stream2.mapToInt(getWeight);
int maxWeight = stream3.max();

And at this point you make bugs easier just from decreased readability and the chance to accidentally use the wrong variable.

Finally, Kotlin wasn’t created in a vacuum: the designers could see that when C# introduced local type inference in 2007, it didn’t lead to significant problems. Or they could look at Scala, which had it since the beginning in 2004; it had (and has) plenty of user complaints, but local type inference isn’t one of them.