Kotlin M12 is out!
We are happy to present Kotlin M12, bringing some rather important changes and new features:
- New syntax for annotations and enums
- More convenient semantics of function types
- Better smart casts
kapt
for Java Annotation Processing support- Multiple IDE features
- and more…
Language
Many of the changes introduced to the language and core libraries are deprecations. Use “Code Cleanup…” action to fix all warnings in your project automatically.
Annotations: New Syntax
As we mentioned before, we decided to reserve square brackets for some more productive future uses, and make annotation syntax more familiar to Java users. So, since M12, we write @Foo(args)
instead of [Foo(args)]
. More details can be found here (even more — in the spec document).
Note that @
is not required in most cases. Normally we write annotations without any escaping:
data class Foo // `data` is an annotation
The old syntax based on [...]
is deprecated, so the compiler will issue warnings on your code. To fix these warnings, press Alt+Enter and run a quick fix (individual or for the whole project). The aforementioned “Code Cleanup…” action also works for the whole project.
Label Syntax Changed
Since M12 @name
is an annotation, but it had a meaning before, i.e. it was a label. We had to find some other syntax for labels, and now they are declared with @
at the end:
loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }
So, loop@
declares a label, and break@loop
uses it.
Class Literals in Annotations
Before M12, annotations in Kotlin were allowed to use java.lang.Class
, for example:
annotation class handledBy(val handlerClass: Class<out Handler>) // Usage handledBy(javaClass<MyHandler>()) class MyEvent {...}
Now, using Java-specific classes is deprecated in Kotlin annotations, and we need to use Kotlin’s own model: kotlin.reflect.KClass
instead of java.lang.Class
and Foo::class
instead of javaClass<Foo>()
:
annotation class handledBy(val handlerClass: KClass<out Handler>) // Usage handledBy(MyHandler::class) class MyEvent {...}
Note that Kotlin sees Java annotations as if they referred to KClass
instead of java.lang.Class
:
// Java @interface JavaAnnotation { Class<?> value(); }
// Kotlin fun introspect(jann: JavaAnnotation) { val theClass = jann.value // the type of this expression is KClass<*> ... }
Now, when we need to turn a KClass
into a java.lang.Class
, we can call .java
on it, e.g. Foo::class.java
or jann.value.java
.
Annotated Primary Constructors
We decided to make primary constructor syntax more regular, and now the full form of the primary constructor includes the constructor
keyword:
class PrivateConstructor private constructor (val x: Int) { ... }
The full form is only needed when we want to annotate a primary constructor or add a modifier. In most cases, the old familiar syntax still works:
class MyClass(val x: Int) { ... }
Traits Are Now Interfaces
As our traits are rather limited anyways, and Java’s interfaces are pretty much the same thing, we have deprecated the trait
keyword, so please use interface
instead.
As usual, quick fixes and “Cleanup Code…” will help you along.
Enum Classes: New Syntax
The new syntax for enums is very close to what Java has. Enum entries should now be separated with commas:
enum class Foo { A, B, C }
Now, when you declare a member of an enum
class, it must go after all entries, and there must be a semicolon after the last entry:
enum class FooWithMember { FOO, BAR, BAZ; fun doIt() { ... } }
Lastly, when enum
has a constructor, you can call it by simply passing the arguments next to the name of the entry:
enum class Color(val rgb: Int) { RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF) // no members => semicolon is not needed here }
The old syntax is deprecated.
Function Types Reformed
We unified function types and extension function types, so that now they can often be used interchangeably. For example, we can pass String::length
where a function '(String) -> Int'
is expected.
// map() expects `(String) -> Int` // argument has type `String.() -> Int` strings.map(String::length)
More details in this post.
If you used Kotlin’s function classes (e.g. kotlin.Function1
) in your Java code, you will need to make adjustments to it, because from now on these classes reside in the kotlin.jvm.functions
package. You can migrate all your Java code by running “Cleanup Code…” with the “Usage of deprecated function classes in Java” inspection.
Smart Casts Made Even Smarter
A long awaited feature: Kotlin can now smart-cast local var
‘s:
var foo = bar() if (foo != null) { foo.baz() // no error here }
Of course, the smart cast only works when the compiler knows that no modification could possibly have happened since the relevant check was made. Note that loops often times distort this picture (due to some technical reasons we can not use a fully-fledged data flow analysis for smart casts), so when a var
is mutated in a loop, smart casts may not work.
Usages of public and protected immutable val
‘s in the same module can also be smart-cast now:
class C(public val d: D?) fun foo(c: C) { if (c.d != null) { c.d.foo() // c.d has been smart-cast here } }
Inlining and Non-Local Returns Supported for Function Expressions
Function expressions introduced in M11 are now supported in inline calls:
fun test(list: List<Foo?>) { val mapped = list.map(fun (item) = item?.toString() ?: return@test) // non-local return ... }
Deprecations and Dropped Features
M12 removes some previously deprecated features:
class object
is dropped in favor ofcompanion object
;init
is now required in front of anonymous initializer blocks.
Some more features were deprecated:
break
andcontinue
inwhen
;- interfaces that extend classes;
- covariant supertype specialization;
- static type assertions.
Use quick-fixes and “Cleanup Code…” to migrate your programs.
Java Interop
jvmOverloads
Kotlin has default arguments that dramatically reduce the needs in overloads, but Java clients can not benefit from this feature directly.
In M12 we have added an annotation jvmOverloads
that tells the compiler to generate N+1 overloads for a Kotlin function that has N default parameters.
For example,
jvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { ... }
will generate
// Java void f(String a, int b, String c) void f(String a, int b) void f(String a)
Source Maps for Better Debugging (JSR-45)
We can now step through bodies of inlined function, thanks to source mapping tables emitted by the compiler into generated class files.
Some technical notes: every class file has line numbers assigned to instructions. For inlined function bodies we assign line numbers beyond the actual end of file (e.g. if the file has 50 lines, inlined code has line numbers starting with 51), and these “virtual” numbers are mapped to actual sources of the inlined code that possibly reside in other files. The standard JVM debugger understands the source mappings and can step through the appropriate files and lines. The only caveat is that exception stack traces may sometimes contain line numbers beyond the end of file. We are looking for a solution to this problem.
Java Annotations: Argument Ordering
In Java, annotations are interfaces and their parameters are methods of those interfaces. Thus, the ordering of the parameters is insignificant, and the call site can not rely on it. This is why Java requires that all arguments but one (named value
) are passed as named.
While annotations declared in Kotlin have proper constructors that admit positioned parameters and even varargs, we can not rely on Java annotations in this respect, so from now on the following applies to Java annotations:
- only the parameter named
value
can be passed without a name, - only the parameter named
value
can be avararg
(and automatically becomes one, if it is an array in Java), - all other parameters can not be passed as positional.
JavaScript
JavaScript back-end is catching up with the JVM one. M12 adds support for
- Inlining works between modules
- Reified parameters
- Function expressions
- Secondary constructors
Tools
kapt: Annotation Processing (JSR-269)
As mentioned earlier in this post, M12 adds initial support for Annotation Processing, so that frameworks like Dagger 2 work with Kotlin now. The main limitation of the current implementation is that Kotlin code can not refer to any declarations generated by the annotation processors (so, for Dagger you need to write at least one small class in Java).
We are going to fix this limitation in the future by generating stubs for classes emitted by the Kotlin compiler. Details in the aforementioned post.
Gradle: JUnit Support for Android
Kotlin Gradle plugin for Android now supports JUnit tests. All we need to do is follow the standard procedure for Java, but now we can write our tests in Kotlin.
Quasar Support
Some of the recent changes in Kotlin enabled a great addition to our ecosystem: now Quasar provides fibers (lightweight threads), Go-like channels, Erlang-like actors, and other asynchronous tools for Kotlin! See the announcement here.
Standard APIs Changed
M12 adds new functionality to the standard library:
- new utilities in
kotlin.io
package - new text utilities
- regular expressions API unified across JVM and JS
- new collection utilities
MIN_VALUE
andMAX_VALUE
available for all numeric types for both JVM and JS
As we are working on the Kotlin standard library, some things get changed and/or deprecated. Use quick fixes and the “Cleanup Code…” action to migrate your code. (Please make sure that you have attached the sources of the standard library to your project.)
The full list of changes is available here.
IntelliJ IDEA Plugin
Kotlin IDE now supports the standard Introduce Parameter refactoring that turns an expression selected inside a function into a parameter:
Additionally, Introduce Lambda Parameter is available to extract a piece of code as a function value:
Rename has an option to rename related declarations (variables, subclasses etc) as well:
As we are adding a lot of deprecations lately, the IDE now supports ReplaceWith
quick-fix: there’s an (optional) extra parameter to the deprecated
annotation, where we can specify an expression to replace a deprecated call:
There is an intention action to add ReplaceWith
to user’s deprecated declarations.
Some more changes:
- New Debugger Features
- Evaluate expression for local functions
- Field Watch Points (only for properties with backing field)
- Change Package Intention
- Highlighting exit points of functions
- Gutter Marks for recursive calls
- Unused receiver parameter inspection
- Code style settings for imports (e.g. we can now always import specified packages with ‘*’)
- Java2Kotlin Converter now offers to update usages in other files
- Typing ‘!’ when completion list is open inserts negated call (e.g.
!foo.isEmpty()
) - Intention actions to change visibility modifiers
- Intention actions have much better scope of availability now
- Quick-fix to add parameters from the base class when the superclass’ constructor has parameters
Things That Haven’t Made It (Yet)
We are still working on some of the changes we announced earlier, such as More Null-safety for Java. Will roll them out as soon as they are ready.
More Announcements Coming
There will be some more M12-related material published in the nearest future. Stay tuned!
Anton says:
May 29, 2015Is there a specific reason why label declaration should include ‘@’ character? You probably could quite successfully use Java syntax for labels, i.e. ‘label:’ – with colon. But maybe I’m missing something?
Andrey Breslav says:
May 29, 2015We don’t want to use
:
, because it will be useful for something else later (most likely, array slices)cypressious says:
May 29, 2015What’s the final syntax for annotation targets? The spec mentions a couple possibilities but none seem to actually work.
Andrey Breslav says:
May 29, 2015Thanks, will fix the spec.
The final syntax is
@file:Foo
or@file:[Foo Bar]
cypressious says:
May 29, 2015I’m sorry, but I’m still confused. My use case is annotating the setter of a delegated property for Dagger2. I need to put @Inject on the setter and @ForDayMonth on the parameter of the setter. Here’s the verbose version without delegation:
Andrey Breslav says:
May 29, 2015Ah, sorry, the extensive targeting that we planned did not make it into M12, the only target supported so far is
file
, other targets are coming latercypressious says:
May 29, 2015Ah that’s too bad. While we’re at it, the spec doesn’t mention setter parameters as possible annotation targets. Could you please consider adding it?
Andrey Breslav says:
May 29, 2015Yes, it’s on the list for consideration
Eddie Ringle says:
May 29, 2015I don’t think this is related to M12 specifically, but it appears that the Kotlin Android Gradle plugin does not work with version 1.3.0-beta1 of the Android Gradle plugin that was released yesterday at I/O. I get the following exception when I try to build with both 1.3.0-beta1 and the M12 plugin:
@Andrey: Just curious, since you’ve deprecated the ‘trait’ keyword, are there any plans to add Rust-style traits to Kotlin, either before 1.0 or beyond? By Rust-style, I guess I mean extending types with entire interfaces at a time, something like:
public interface HasThing {
var thing: Thing
}
impl HasThing for ExistingClass {
override var thing: Thing = Thing()
}
Andrey Breslav says:
May 30, 2015Please report bugs to the tracker: https://youtrack.jetbrains.com/issues/KT
Eddie Ringle says:
May 30, 2015Sure! I submitted it as KT-7884.
Can you comment on my question regarding traits? 🙂
Andrey Breslav says:
May 30, 2015There are no plans to do it before 1.0, we may consider something like this later.
Fritz says:
May 30, 2015Isn’t it about time to release a 1.0? I’ve been following Kotlin for years now and the language seems quite interesting, but I cannot use a language that is not released yet for my company’s products. Any timeframe other than when it’s ready it’s ready?
Andrey Breslav says:
May 30, 2015Unfortunately, all we can say for sure is “it’s ready when it’s ready”. You can see how many breaking changes have been made in this milestone, there’s no way something like this can happen to a released language. We are finalizing the language, but it will take us some more time.
M Platvoet says:
May 31, 2015Great work guys, I really like the fact that you dare to reconsider features.
One of those reconsiderations, so it seems, is that Kotlin now issues a warning
Package directive doesn’t match file location
Does this mean that it using non-matching locations isn’t supported anymore in the near future?
I like the fact that they didn’t have to match as it cuts down on all those “useless” intermediate directories. What is the motivation for this?
Andrey Breslav says:
May 31, 2015This is not mandated by the compiler, just an IDE inspection. You can switch it off, and forget about it. We are not planning to remove the ability to keep files where you like
M Platvoet says:
June 1, 2015Good to hear, thanks
Antonio says:
June 1, 2015I have used kotlin now for 3 years and I think is a great language but I am a little bit concerned about the direction the language is taking. For example: is there a reason why you changed the String.split(…) method? Sounds like you are trying to patch some decisions you made in the past. Please advise.
Andrey Breslav says:
June 2, 2015We are cleaning up some previously made mistakes. The thing with
split()
is that we want to make it consistent with the Kotlin motto of making important things explicit: if something creates a complex Regex object, it should be visible in the code, that’s why we are changing the contract ofsplit()
.Új verzió jelent meg a Java utódjának szánt programozási nyelvből - Hírek - Prog.Hu says:
June 2, 2015[…] napokban újabb mérföldkő jelent meg a JetBrains saját programozási nyelvéből, amire a cég szerint a Java egyfajta utódjaként […]
Dale King says:
June 4, 2015The addition of the .java for getting the Java class has some issues related to the imports and can fail to compile with a different ordering of imports.
Start with something like this:
which adds an import:
then try auto importing a java class
val now = Date()
And use the IDE to select java.util.Date (e.g. alt-enter)
It will not add an import of java.util.Date, but instead will change that to:
But that doesn’t compile because it thinks java is referring to the one in reflection.
You have to manually add the java.util.Date import.
I can’t seem to reproduce it now, but I thought I saw issues where it would fail to compile if the java.util.Date import was after the java import.
Andrey Breslav says:
June 4, 2015Looks like a bug, we’ll investigate, thanks!
Yested FW updated to Kotlin M12 | Yested Framework says:
June 4, 2015[…] Yested FW was updated to Kotlin M12. […]
Даниил Водопьян says:
June 5, 2015http://kotlinlang.org/docs/reference/enum-classes.html#working-with-enum-constants
Why does it throw an exception rather than make use of kotlin explicit nullability? As I see it, any method that throws exactly one (obvious) exception, which is expected to be handled, should return a nullable value. The benefits are 1) explicitness in the method signature 2) mandatory fail checking 3) shorter user code. It is not the first time I see such a decision in stdlib. Please explain the cons.
Andrey Breslav says:
June 5, 2015Not unlike many other things, this is a matter of compatibility with Java: Java’s valueOf() behaves this way, and I don’t think we can alter this behaviour in a sane way
Даниил Водопьян says:
June 5, 2015Is it a technical issue (like binary compatibility), or some indulgence for “java-code-copy-pasting”?
Andrey Breslav says:
June 5, 2015“Indulgence”? This sounds a little bit too opinionated for a discussion of an engineering decision.
Даниил Водопьян says:
June 6, 2015Well, maybe 🙂 Don’t take me wrong – sometimes I disagree with your design decisions, sometimes I am surprised by them. Here I am surprised.
I am so concerned about that because it is stdlib, and I believe that kotlin stdlib must be as good/sane/elegant as possible.
Look at Scala – the language may be questionable, but the library sucks, and nothing is more frustrating than that.
Dale King says:
June 6, 2015I agree that returning null would be a better API than throwing an exception but that is the API that Java has. There is no getting around the fact that if you call it with an invalid value an exception will get thrown. The only way Kotlin could change that is wrapping the call with a try catch and using a null instead. I don’t think I want Kotlin going that far to change the questionable decision of the Sun API as the default implementation when in reality for most people it really will be an error condition.
At most I would want them to add a safeValueOf function to request this explicitly or a more fancy implementation would be to figure out if the destination for the value is nullable or not and choose based on that.
Даниил Водопьян says:
June 6, 2015@Dale, yes, exactly! But think of this – in Java it was a
checked
exception, because everybody must check on parsing an input. The closest alternative in Kotlin is a checked null!Vadzim says:
June 5, 2015I’m using kotlin 0.12.213 and I noticed that my final apk contains a directory with a *.kotlin_class files. And I’m wondering what is the purpose of these files, and why do I need to keep them?
Alexander Udalov says:
June 8, 2015Good question. These files are used by Kotlin to figure out what declarations are present in the built-in packages (kotlin, kotlin.reflect) of the Kotlin runtime library which your code was compiled against. They’re useful in exactly two cases: when you’re shipping a library to be used by other Kotlin applications (which is clearly not the case for APK apps), or you’re using Kotlin reflection in your code. If it’s neither, you can safely strip these files from your app.
Dale King says:
June 6, 2015While the annotation processing is great, I have found that one are where Kotlin is safer leads to problems with many uses of annotations. Kotlin properties generate getters and setters with a private field. In an ideal world that is the way things should work and no one outside of the class should be able to access the field.
But unfortunately, Java has not been so strict and there are many cases of consumers of annotations (both in APT and at runtime) that expect the ability to declare an annotated NON-PRIVATE field. Here are ones that I have seen:
Luckily dagger2 and Junit rule have a work around with annotating the get/set (which is much clumsier particular when you need qualifiers), but many other libraries don’t have that option.
Ideally, those libraries would be changed to support annotating get/set methods (and I have requested that on some of them) but that is a long time coming. Unfortunately this leads to difficulties switching to Kotlin.
What I propose is to add an annotation that can be applied to properties that tells the Kotlin code generator to weaken the encapsulation on the generated backing field to allow access to the raw field from outside the class. This has to be explicitly enabled with the annotation so the default is to make the fields private.
As other libraries catch on to Kotlin this will be less necessary, but would be a great aid as the world slowly starts supporting Kotlin.
Andrey Breslav says:
June 7, 2015Yes, we are going to support such an annotation
Agustin says:
June 20, 2015Smart cast are not working at all (Using ‘org.jetbrains.kotlin:kotlin-gradle-plugin:0.12.412’) :/
class A
{
protected var mBuffer: ByteBuffer? = null
}
Andrey Breslav says:
June 20, 2015This works as expected: the compiler can’t make sure that
mBuffer
does not change between the check and the call tocapacity()
, because another thread could change it, or if the code was a little more complex, another call on the same thread. So, smart casts are unsound for properties, and thus do not work for them. Suggestion: use thelet
functionAleksandr Dubinsky says:
July 3, 2015Could you explain why we want the compiler to make sure that mBuffer is not changed by another thread? If there is multi-threaded access of mBuffer, then many kinds of statements (such as,
if (!mBuffer.isEmpty()) mBuffer.take()
) become very dangerous, yet the compiler does not guard us from such things. Why are smart casts different?Andrey Breslav says:
July 5, 2015First of all, it may be changed on the same thread and there’s no way to track that either.
The type system must guarantee something (in this case that the absence on a NPE at runtime). There’s no such guarantee for the semantics of
isEmpty()
, because it’s out of the scope of the compiler checks to guarantee complex properties on the library code, but there is one for smart casts, because they are a built-in mechanism in the language.Vitaliy says:
June 30, 2015As mentioned above traits are deprecated now. And as I can see from Idea help messages specifying a required base class for interface implementations is deprecated too. Are there some possibilities to specifying a required base class now or this functionality will be removed at all?
Andrey Breslav says:
June 30, 2015Traits are not deprecated, only the name is being changed to more familiar “interface”. Required base classes will not be supported in Kotlin 1.0
Vadzim says:
July 14, 2015How can I access a protected field from the superclass written in Java? It gives me ‘Unresolved reference’ error. Tried both
this.mField
andthis.$mField
.Andrey Breslav says:
July 14, 2015Works for me. Could you share your code?
Vadzim says:
July 14, 2015I’m using ‘0.1-SNAPSHOT’
class FocusView : View {
jvmOverloads constructor(context: Context, attrs: AttributeSet, defStyle: Int = 0)
: super(context, attrs, defStyle) {
}
}
Andrey Breslav says:
July 14, 2015And what is the Java class declaration? Which packages and modules are both classes in?
It would be most convenient if you filed an issue with a small repro example here: https://youtrack.jetbrains.com/issues/KT
Vadzim says:
July 14, 2015Problem solved. This protected property has a @hide annotation and it is not present in the stub version of Android runtime.
Sorry for bothering you.
Roadmap de #kotlin | Kotlin.es says:
August 22, 2015[…] oficial en inglés, […]
Debug logging in Kotlin | Kotlin for Android says:
September 28, 2015[…] exception stack traces may sometimes contain line numbers beyond the end of file, as stated in the M12 release notes in the Source Maps for Better Debugging (JSR-45) section. I was able to reproduce this behaviour […]