1. Data class
Language designers hardly ever agree on the necessity and the feature scope of what a class is. In Java, curiously, every class always has identity a concept that is not really needed in 80% – 90% of all real world Java classes. Likewise, a Java class always has a monitor on which you can synchronize. In most cases, when you write a class, you really just want to group values, like Strings, ints, doubles. For instance:
public class Person {
final String firstName;
final String lastName;
public JavaPerson(...) {
...
}
// Getters
...
// Hashcode / equals
...
// Tostring
...
// Egh...
}
data class Person(
val firstName: String,
val lastName: String
)
hashCode()
, equals()
, toString()
is obvious and can be provided by default. Furthermore, data classes are first class tuples, so they can be used as such, e.g. to destructure them again in individual references:
val jon = Person("Jon", "Doe")
val (firstName, lastName) = jon
val
is possible in Kotlin: Local variable type inference. This is being discussed for a future Java version right now.
2. Defaulted parameters
How many times do you overload an API like the following:
interface Stream<T> {
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
}
Stream
operations. The first one simply applies Comparator.naturalOrder()
to the second one. So we could write the following, in Kotlin:
fun sorted(comparator : Comparator<T>
= Comparator.naturalOrder()) : Stream<T>
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
reformat(str)
reformat(str, true, true, false, '_')
reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
3. Simplified instanceof checks
If you will, this is really an instanceof switch. Some people may claim that this stuff is evil, bad OO design. Nja nja. I say, this happens every now and then. And apparently, in Java 7, string switches were considered sufficiently common to modify the language to allow them. Why not instanceof switches?
val hasPrefix = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
when
expression is powerful. You can mix any sort of predicate expressions, similar to SQL’s CASE
expression. For instance, this is possible as well:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
CASE x
WHEN BETWEEN 1 AND 10 THEN 'x is in the range'
WHEN IN (SELECT * FROM validNumbers) THEN 'x is valid'
WHEN NOT BETWEEN 10 AND 20 'x is outside the range'
ELSE 'none of the above'
END
4. Map key / value traversal
Now this could really be done very easily only with syntax sugar. Granted, having local variable type inference would already be a plus, but check this out
val map: Map<String, Int> = ...
for ((k, v) in map) {
...
}
Map.entrySet()
. Map could have been enhanced to extend Iterable<Entry<K, V>>
in Java 5, but hasn’t. That’s really a pity. After all, it has been enhanced in Java 8 to allow for internal iteration over the entry set in Java 8 via Map.forEach()
:
map.forEach((k, v) -> {
...
});
Map<K, V> extend Iterable<Entry<K, V>>
5. Map access literals
This one is something that would add tons and tons of value to the Java language. We have arrays, like most other languages. And like most other languages, we can access array elements by using square brackets:
int[] array = { 1, 2, 3 };
int value = array[0];
val map = hashMapOf<String, Int>()
map.put("a", 1)
println(map["a"])
x[y]
is just syntax sugar for a method call backed by x.get(y)
. This is so great, we have immediately proceeded with renaming our Record.getValue()
methods in jOOQ to Record.get()
(leaving the old ones as synonyms, of course), such that you can now dereference your database record values as such, in Kotlin
ctx.select(a.FIRST_NAME, a.LAST_NAME, b.TITLE)
.from(a)
.join(b).on(a.ID.eq(b.AUTHOR_ID))
.orderBy(1, 2, 3)
.forEach {
println("""${it[b.TITLE]}
by ${it[a.FIRST_NAME]} ${it[a.LAST_NAME]}""")
}
it[b.TITLE]
is a String expression. Great, huh? So, not only can this syntax be used with JDK maps, it can be used with any library that exposes the basic get()
and set()
methods.
Stay tuned for more jOOQ and Kotlin examples here:
https://github.com/jOOQ/jOOQ/blob/master/jOOQ-examples/jOOQ-kotlin-example/src/main/kotlin/org/jooq/example/kotlin/FunWithKotlinAndJOOQ.kt
6. Extension functions
This one is a controversial topic, and I can perfectly understand when language designers stay clear of it. But every now and then, extension functions are very useful. The Kotlin syntax here is actually just for a function to pretend to be part of the receiver type:
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)
Stream
API by wrapping it in a jOOλ type (another such library is StreamEx, with a slightly different focus). The jOOλ Seq
wrapper type is not really important, as it pretends to be a Stream
on steroids. It would be great, if jOOλ methods could be put onto Stream
artificially, just by importing them:
list.stream()
.zipWithIndex()
.forEach(System.out::println);
zipWithIndex()
method isn’t really there. The above would just translate to the following, less readable code:
seq(list.stream())
.zipWithIndex()
.forEach(System.out::println);
stream()
. For instance, you could then do:
list.zipWithIndex()
.forEach(System.out::println);
Iterable
.
Again, this is a controversial topic. For instance, because
While giving the illusion of being virtual, extension functions really are just sugared static methods. It’s a significant risk for object oriented application design to engage in that trickery, which is why this feature probably won’t make it into Java.
7. Safe-call operator (and also: Elvis operator)
Optional is meh. It’s understandable that anOptional
type needed to be introduced in order to abstract over the absence of primitive type values, which cannot be null. We now have things like OptionalInt
, e.g. to model things like:
OptionalInt result =
IntStream.of(1, 2, 3)
.filter(i -> i > 3)
.findFirst();
// Agressive programming ahead
result.orElse(OR_ELSE);
flatMap()
the absent value.
o_O
Sure, if you want to do sophisticated functional programming, you’ll start typing map()
and flatMap()
everywhere. Like today, when we’re typing getters and setters. Along will come lombok generating flatmapping calls, and Spring will add some @AliasFor
style annotation for flatmapping. And only the enlightened will be able to decipher your code.
When all we needed was just a simple null safety operator before getting back to daily business. Like:
String name = bob?.department?.head?.name
Optional<String> name = bob
.flatMap(Person::getDepartment)
.map(Department::getHead)
.flatMap(Person::getName);
String name = null;
if (bob != null) {
Department d = bob.department
if (d != null) {
Person h = d.head;
if (h != null)
name = h.name;
}
}
8. Everything is an expression
Now this might just be a unicorn. I don’t know if there is a JLS / parser limitation that will forever keep us in the misery of prehistoric distinction between statement and expression. At some point in time, people have started using statements for things that yield side-effects, and expressions for more functional-ish things. It is thus not surprising, that allString
methods are really expressions, operating on an immutable string, returning a new string all the time.
This doesn’t seem to go well with, for instance, if-else
in Java, which is expected to contain blocks and statements, each possibly yielding side-effects.
But is that really a requirement? Can’t we write something like this in Java as well?
val max = if (a > b) a else b
?:
. But what about Kotlin’s when
(i.e. Java’s switch
)?
val hasPrefix = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
boolean hasPrefix;
if (x instanceof String)
hasPrefix = x.startsWith("prefix");
else
hasPrefix = false;
?:
. I just find if-else
easier to read, and I don’t see why that should be a statement, not an expression. Heck, in Kotlin, even try
is an expression, not a statement:
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}
9. Single expression functions
Now this. This would save so much time reading and writing simple glue code. And in fact, we already have the syntax in annotations. Check out Spring’s magical @AliasFor annotation, for instance. It yields:
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
}
default
is weird, given that it was not re-used in Java 8 for default methods, but I guess Java always needs the extra syntax so developers feel alive as they can better feel their typing fingers. That’s OK. We can live with that. But then again, why do we have to? Why not just converge to the following?
public @interface AliasFor {
String value() = "";
String attribute() = "";
}
// Stop pretending this isn't an interface
public interface AliasFor {
String value() = "";
String attribute() = "";
}
10. Flow-sensitive typing
Now this. THIS! We’ve blogged about sum types before. Java has sum types with exceptions since Java 7:
try {
...
}
catch (IOException | SQLException e) {
// e can be of type IOException and/or SQLException
// within this scope
}
when (x) {
is String -> println(x.length)
}
x is String
. Conversely, in Java:
if (x instanceof String)
System.out.println(((String) x).length());
11. (Bonus) Declaration site variance
Last but not least, better generics via declaration site variance. Many other languages know this, for instance also C#’sIEnumerable
:
public interface IEnumerable : IEnumerable
The keyword out
here means that the generic type T
is produced from the type IEnumerable
(as opposed to in
, which stands for consumption). In C#, Scala, Ceylon, Kotlin, and many other languages, we can declare this on the type declaration, rather than on its usage (although, many languages allow for both). In this case, we say that IEnumerable
is covariant with its type T
, which means again that IEnumerable<Integer>
is a subtype of IEnumerable<Object>
In Java, this isn’t possible, which is why we have a bazillion question by Java newbies on Stack Overflow. Why can’t I…
Iterable<String> strings = Arrays.asList("abc");
Iterable<Object> objects = strings; // boom
Iterable<String> strings = Arrays.asList("abc");
for (Object o : strings) {
// Works!
}
Stream
:
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Function
or Stream
would be:
interface Function<in T, out R> {}
interface Stream<out T> {}
? super
and ? extends
garbage could be removed without losing any functionality.
In case you’re wondering what I’m even talking about? :)
The great news is, this is being discussed for a (near) future version of Java:
http://openjdk.java.net/jeps/8043488
“?.” is only called the Elvis operator in C# while in Kotlin it’s called the safe-call operator. In Kotlin, “?:” is the Elvis operator and it has the same number of eyes as the actual Elvis, unlike the C# one.
Thank you very much for the clarification. I have edited that section to reflect your input.
The paragraph is still titled “Elvis operator” (which is also a neat feature, Java could steal).
OKOK :)
What I really like about point 3 is that you can use it with enums, and, most importantly, the compiler will refuse to compile if one of the enum constants is not handled. This allows adding an enum constant safely. Sure, we can add a method to the enum itself, but it’s not always possible, or even desirable.
Oh, very interesting, I didn’t know that. Do you have a link to the relevant Kotlin docs that shows this behaviour?
A lot of this is covered in Project Lombok – Can’t use Java without it anymore
I suspect I should write a top 10 lombok features article…
A better world’s Java would include lombok. I’d also prefer something build-in to all the insanity lombok has to do in order to keep me sane and save my fingers.
Sure, but AFAIK they could be made virtual if someone really wanted. Just let the compiler generate a top-level extension method doing virtual dispatch using an instanceof switch to one of the applicable user-defined extension methods. This could probably lead to too many surprises as the methods reside in other source files and can get forgotten.
One might be tempted to claim that all very popular libraries should be rendered obsolete by new Java versions. Much of Guava would qualify too, for instance.
Hmm, there’s a lot of potential for conflicts, this way. Let’s assume that there are two completely independent
void x();
extension methods on, say,java.lang.Object
. If they were actually added to Object using virtual dispatch, which implementation would win in general?With static extension methods, it doesn’t matter. Object is not modified, and the method call sugar is only locally applied to the individual call sites. Or, perhaps, I’m missing something?
Sure, and this is actually happening, albeit rather slowly and not always in the best possible way (e.g., com.google.common.base.Objects.equal can be statically imported, unlike java.util.Objects.equals).
Can you do this in a single project? I see, you “import” just the one you want.
Dynamic extension methods would not modify existing classes either. They’d just get aggregated into a dispatching extension method. Somehow.
and the method call sugar is only locally applied to the individual call sites.
Now, I can see the big mess resulting from my idea. On every call site, I’d collect all “imported” extension methods and aggregate them. You forget one and get a nasty surprise.
When you imported these two dynamic extension methods
you’d get this static extension method generated and used instead of them
On a different call site with different extensions, you could get something else. Pretty strange. Not really dynamic.
I guess, it could work with some restrictions… but I’m not sure if these restrictions can be sane.
Hmm, I’m not so sure how these are “dynamic”, or different from “ordinary” extension methods. I suspect that with the right design, such behaviour can still be achieved with classic “static” extension methods. But I might still be missing your point :)
They’re different by the dynamic dispatch. What I wrote was a possible implementation of the dynamic dispatch using ordinary extension methods and one generated method per method signature.
If there were a single non-conflicting set extension methods used globally, it’d be a “perfect dynamic dispatch”, working like if the methods were defined in the class itself (except for visibility restrictions, but let’s call it a feature).
This could be usable. Instead of using
you’d configure your project by specifying a set of (classes specifying) extension methods to be used everywhere. Let’s say in extension-info.java. I hope, the “biggest of all hacks” is gonna offer such a thing soon (albeit with static dispatch).
Your example of “two completely independent void x();” would be as illegal as defining void x(); in a class twice. This should be no real problem, given that you want x to mean the same thing in the whole project. Just choose one x and stick with it.
About point 8. Not everything is expression. Assignment is not. An old legacy of C/C++ got fixed.
Really? Interesting, thanks for pointing this out. Well, I guess if val is the default, then you can’t have assignment expressions. I do find that quite useful at times, though, in Java (although I agree it doesn’t really add to readability)
IMO using an assignment as expression is a code smell (or a lack of the right language features for the job). For things like reading from a stream where you typically do `while (len = r.read(chars)) >= 0)` in java, you can use higher order functions like `Reader.forEachLine{ line -> /* do something with line */ }` in Kotlin.
Not sure… This is a typical idiom how to read / write streams in Java:
I don’t think there’s anything particularly smelly about this example, given the historic context of special return values being encoded into primitive type ints.
That, I completely agree with.
You can do that as well in Java:
So, I guess we agree. Given historic context, assignment expressions made sense. But with more modern APIs, we don’t really need that anymore.
Agreed.
I agree with a lot of these, but not the null coalescing thing.
Dealing with nulls SHOULD be painful so that people who return nulls will be gently encouraged by other developers to not return nulls.
In SQL you don’t have nulls as return values. You can have empty results, but you don’t get null. (Yeah, you can have them as a value. It’s annoying there too. Pattern anyone?)
The problem is that we see null, see that it’s annoying and we want some sugar for it. But I think that a better solution would be to do what Common Lisp did with the null. If we can’t get there, then nulls must be painful. Nulls must HURT. That way, people don’t write return null, because people will gently tie them up and beat them up until they stop doing that. Returning null is evil. Returning Optional is good. Even if people just do Optional.getOrThrow(() -> throw new ReasonableExceptionType(complaintString));
They will deal with no value returns immediately, and then we can move on with our lives… :p
SQL NULL isn’t really annoying, it’s quite elegant (apart from the fact that it is a bit inconsistent across the language) and has nothing to do with Java’s null. It means UNKNOWN with all the nice properties of three-valued logic associated with that. But yes, I agree. Otherwise, the fact that SQL operates on sets which can never be null is quite convenient. There are programming languages, where everything is an array, too…
Anyway, I think what Kotlin did is quite reasonable on the JVM. As long as we’re running on the JVM, we have to live with null.