Kotlin
A concise multiplatform language developed by JetBrains
Kotlin 1.0 Beta Candidate is Out!
We are happy to present Kotlin Beta Candidate. An official 1.0 Beta will be out soon. By now, the binary format is finalized, no major language changes are planned, and only a few changes in the standard library are coming.
In this post we describe the changes since M14, including
- imports from objects,
- new safer collection interfaces,
- inlining for Java constants,
- better support for Java statics,
- and more.
Language changes
We are rolling out some breaking changes along with new important features.
Operators and infix functions
Since M14, Kotlin requires the operator
modifier on functions that are used for operator overloading. From now on the same is required for infix functions:
operator Foo.plus(other: Foo): Foo { ... } fun testPlus() = Foo() + Foo() infix fun Foo.bar(other: Foo): Foo { ... } fun testInfix() = Foo() bar Foo()
Now, we have relaxed this requirement for Java functions: any Java function with a suitable signature can be used as an operator, but not as infix.
Some operator names have been changed to avoid ambiguities:
- we should now use
unaryPlus
andunaryMinus
instead of justplus
andminus
for unary functions, i.e.-Foo()
is nowFoo().unaryMinus()
; - for delegated properties,
getValue
andsetValue
should be used instead of justget
andset
.
The Code cleanup action will help you migrate your code.
Also, operator signatures are now checked by the compiler at the declaration site. Some of these checks may be relaxed in the future, but we believe that what we have now is a pretty good starting point.
Imports from objects
Kotlin now supports importing individual members of objects by name (but not *
-imports from objects):
import mypackage.MyObject.foo val test = foo("...")
In this example we imported all members named foo
from the named object mypackage.MyObject
.
To import from companion objects of classes, we have to specify their full name:
import mypackage.MyClass.Companion.foo
Rich @Deprecated
We have been migrating a lot of code lately :) So Kotlin’s @Deprecated
annotation has become really powerful: not only does it require a message and allow to specify a replacement through ReplaceWith("...")
, it also has a level
now: WARNING
, ERROR
or HIDDEN
.
WARNING
is default and works as a normal deprecation: there will be warnings at call sites, and the IDE will strike it out,ERROR
is the same, but a compilation error is reported instead of a warning,HIDDEN
is what previously was@HiddenDeclaration
: it simply makes this declaration invisible to clients at compile time.
Smart casts for captured local var’s
Smart casts now work even on local var
‘s that are captured in lambdas, if they are not mutated in those lambdas:
var a: Any? = ... val mapped = users.map { "${it.name}: $a" } if (a is String) { println(a.length) // This works now }
Multiple main() functions in the same package
We can now define a main()
function with standard signature in each file (with the exception of @file:JvmMultileClass
). This is very useful when experimenting with code:
// FILE: a.kt package foo fun main(args: Array<String>) { println("a.kt") } // FILE: b.kt package foo fun main(args: Array<String>) { println("b.kt") }
Varargs and spread operator
To recap: when calling a vararg
function, we can use the spread operator that converts an array to a vararg:
fun foo(vararg args: String) { ... } fun bar(vararg args: String) { foo(*args) // spread operator }
The semantics of the spread operator have been fixed so that it always guarantees that an array that foo
sees will not be modified or observed by the “outside world”. We can assume that a defensive copy is made every time the spread operator is used (in fact, some optimizations may be implemented later to reduce memory traffic).
As a result, authors of Kotlin libraries can rely on the vararg arrays being safe to store without defensive copying.
NOTE: This guarantee is not fulfilled when Kotlin functions are called from java, because no spread operators are used there. This means that if a function is intended to be used from both Java and Kotlin, its contract for Java clients should include a note that the array should be copied before being passed to it.
“sparam” annotation target has been renamed to “setparam”
To annotate a setter parameter of a property, use setparam
use-site target instead of sparam
:
@setparam:Inject var foo: Foo = ...
@UnsafeVariance annotation
Sometimes we need to suppress declaration-site variance checks in our classes. For example, to make Set.contains
typesafe while keeping read-only sets co-variant, we had to do it:
interface Set<out E> : Collection<E> { fun contains(element: @UnsafeVariance E): Boolean }
This puts some responsibility on the implementor of contains
, because with this check suppressed the actual type of element
may be anything at all at runtime, but it’s sometimes necessary to achieve convenient signatures. See more on the type-safety of collections below.
So, we introduced the @UnsafeVariance
annotation on types for this purpose. It’s been deliberately made long and stands out to warn agains abusing it.
Miscellaneous checks and restrictions
Many checks were added, some of these restrictions may be lifted later.
Type parameter declarations. We decided to restrict the syntax of type parameter declarations so that all such declarations are consistent, so
fun foo<T>()
is deprecated in favor offun <T> foo()
:- All constraints on type parameters should occur either in “where” or inside “<…>”:
fun <T: Any> foo() {} // OK fun <T> foo() where T: Serializable, T: Comparable<T> {} // OK fun <T: Serializable> foo() where T: Comparable<T> {} // Forbidden
Dynamic type checks for arrays. Array element types are reified in Java, but their Kotlin-specific properties, like nullability, is not. So, we removed the special treatment of arrays that allowed checks like a is Array<String>
, and now arrays work as all other generic classes: we can check for a is Array<*>
and a cast like a as Array<String>
is marked as unchecked. We added a JVM-specific function isArrayOf<T>()
that check that a given array can contain elements of type T
in Java:
val a: Any? = ... if (a is Array<*> && a.isArrayOf<String>()) { println((a as Array<String>)[0]) }
Delegated properties. The conventions for delegated properties now use KProperty<*>
instead of PropertyMetadata
in getValue
and setValue
:
fun Foo.getValue(thisRef: Bar, property: KProperty<*>): Baz? { return myMap[property.name] }
Code cleanup will help you migrate.
Callable references. Some usages of ::
are forbidden for now, to be enabled later when we implement bound references. Most notably, ::foo
should not be used for now when foo
is a member of a class. Only MyClass::foo
should be used. References to members of objects are also unsupported temporarily (they will work as bound references too). We can use lambdas as a workaround for the time being.
If-expressions. We unified the semantics of if
and when
by requiring an else
when if
is used as an expression:
val foo = if (cond) bar // ERROR: else is required
Nothing-returning functions. When a function is known to throw an exception or loop forever, it’s return type may be Nothing
, which means that it never returns normally. To make the tooling smarter, we require that such functions always have their return type specified explicitly:
fun foo() = throw MyException() // Must specify return type explicitly fun bar(): Nothing = throw MyException() // OK fun baz() { throw MyExcepion() } // OK fun goo(): Goo { throw MyExcepion() } // OK
This is now a warning that will be promoted to error after we migrate our code with Code cleanup
Visibility checks were restricted so that, for example, a public declaration can not expose a local, private or internal type. Access to internal declarations is checked in the compiler as well as in the IDE;
See more here.
Collections
The major change in this version is that we have cleaned up collections and other core APIs so that, for example, size
is now a property, and contains
is type-safe: it takes E
instead of Any?
. This has been a major effort to make the library feel like Kotlin while keeping it compatible with Java. There’s quite some compiler magic behind it, but we are pleased with the result.
Example:
val strs = setOf("1", "abc") if (1 in strs) { // 'strs' is a set of strings, can't contain an Int println("No!") }
Analogous code works in Java, because Set<E>.contains
(which in
is compiled to) takes Object
, not E
, the element type of the set. This has proven to be error-prone, so we decided to make Kotlin collection interfaces safer (while keeping full compatibility with Java collections). As a result, our contains
takes an E
, and the example above is incorrect in Kotlin.
At the moment the Kotlin compiler reports a deprecation warning on in
in the example above, because we have provided transitional extension functions in the standard library to help everyone migrate, but soon this will be an error. Code cleanup is our friend here: it will replace 1 in strs
with strs.containsRaw(1)
. containsRaw
is a new function in the standard library that we can use when we really need the Java-like behavior: we can check membership of any object in any set by using containsRaw
.
Bottomline:
Collection.contains
,Map.get
and some other collection methods are now safer;- We can use
containsRaw
,getRaw
, etc to get the untyped behavior; Collection.size
,Array.size
,String.length
,Map.Entry.key
etc are now properties;List.remove(Int)
has been renamed toremoveAt(int)
to avoid clashes withList<Int>.remove
that removes by item, not by index;- Code cleanup will migrate all the code.
All normal Java collections work without changes: the compiler knows how to find a “property” size
on a java.util.ArrayList
.
Java interop
There have been many important changes that concern how Kotlin declarations are visible from Java and vice versa.
Inlining constants defined in libraries
From now on we inline Java constants (public static final fields of primitive and String types) that come from libraries. This will help Android developers that have been suffering from API incompatibilities:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ... }
Will now work on any version on Android runtime (used to crash on runtimes younger than Lollipop).
Smaller runtime
We are only starting there, but the foundation has been laid for the work on reducing the size of the kotlin-runtime
library. It is now only 200K smaller than it was in M14, but there are more things we’ll do to make it smaller (and it won’t break compatibility).
Static methods, fields and classes
Kotlin is now very friendly to Java statics:
- we can use inherited nested classes, static methods and fields from Java classes inside their Kotlin subclasses;
- we can access inherited Java static methods and fields through subclass name:
SubClass.SUPER_CLASS_CONSTANT
; - we can access members of companion objects of superclasses from within Kotlin subclasses;
- but the only qualified name a class can be accessed by is its canonical name, i.e. we can’t say
SubClass.SupersInnerClass
.
This closes many issues we used to have with big inheritance-based frameworks like Android.
Interface inheritance rules are compatible with Java 8
To make Kotlin future-proof, we added some requirements that comply with the Java 8’s ones, to be able to later compile function bodies in Kotlin interfaces to Java default methods.
In some cases it leads to Kotlin requiring more explicit overrides than before, and, sadly, methods of Any
can’t be implemented in interfaces any more (this wouldn’t work in Java 8).
Side note: the default implementations of interface methods are accessible from Java through as static members of MyIntf.DefaultImpls
.
More convenient getter names for booleans
When a property in Kotlin is, for example, named isValid
, its Java getter will now be isValid()
and not getIsVaild()
.
@JvmField and objects
We have made the strategy for generating pure fields (as opposed to get/set pairs) more predictable: from now on only properties annotated as @JvmField
, lateinit
or const
are exposed as fields to Java clients. Older versions used heuristics and created static fields in objects unconditionally, which is against our initial design goal of having binary-compatibility-friendly APIs by default.
Also, singleton instances are now accessible by the name INSTANCE
(instead of INSTANCE$
).
We had to prohibit the usage of @JvmField
in interfaces, because we can’t guarantee proper initialization semantics for them.
Int is Serializable
Now the type Int
and other basic types are Serializable
on the JVM. This should help many frameworks.
No “package facades”
Classes like KotlinPackage
etc are gone. We have finished the transition onto the new class-file layout, and the previously deprecated “package facades” are now removed. Use FileNameKt
and/or @file:JvmName
(with the optional @file:JvmMultifileClass
).
Internals are now mangled
Since Java doesn’t have internal
visibility (yet), we had to mangle the names of internal
declarations to avoid unexpected clashes in overrides when we extend a class from another module. Technically, internal members are available to Java clients, but they look ugly, which is the minimal price we could pay for predictability of library evolution.
Other deprecations and restrictions
@Synchronized
and@Volatile
are not applicable for abstract declarations;- As the final step of getting rid of external annotations,
@KotlinSignature
is deprecated and will be removed; - Generic types whose arguments contains
Nothing
are compiled to raw Java types now, since Java doesn’t have a proper counterpart of `Nothing;
IDE Changes
- Now Parameter Info works almost everywhere, including brackets, this and super calls
-
Completion now supports callable references (after ::)
-
We can easily generate equals() and hashCode() methods
-
Also the IDE helps generate Secondary Constructor based on superclass constructor and currently uninitialized properties
-
Support for configurable “import *” for importing static members of Java classes and enums
-
The last but not least, unit testing experience is much smoother now. List of improvements: “Create Test” action, tests are runnable via gutter icons, navigation between tests and test subjects (⇧⌘T/⇧^T) and also quickfixes to add dependencies on JUnit and TestNG when needed
Libraries
Thanks to the aforementioned changes in spread operator semantics, listOf()
now is more efficient, because is doesn’t have to defensively copy the input array.
Regex
API has been improved, so that we can now say regex in string
instead of regex.hasMatch(string)
.
Tools
- Compiler daemon is now enabled by default in the IDE
- Parallel Compilation of Independent Modules is now supported in the daemon
- Options related to external annotations are removed from tools like Maven and Gradle
Installation
Kotlin is bundled with IntelliJ IDEA 15 RC. For IntelliJ IDEA 14, please update through the Plugin Manager.
Have a nice Kotlin!