Call for Feedback: Java Statics, Result Expressions and More

Andrey Breslav

Thank you all for the feedback we got on the previous call! Here comes another round of changes and adjustments. Your opinions and use cases are welcome.

Java Statics and Inheritance

We are going to improve interoperability with Java statics in Kotlin by allowing Kotlin subclasses to access static members of their superclasses: we will now be able to use constants, nested classes and static utilities defined up the inheritance tree. Same for members of companion objects of supertypes.

We will also allow calling Java statics on Java classes that inherit them.

What won’t be allowed is calling an inherited static member on a Kotlin class:

// Java
public class Base {
    public static void foo() {}
}

// Kotlin
class Derived : Base() {
    fun test() {
        foo() // OK
        Base.foo() // OK

        Derived.foo() // ERROR!
    }
}

Lateinit val’s

With the help of our users we found that we have previously missed an unpleasant hole in the design of lateinit val: backing fields for such properties were not final, and thus Java code could modify them freely. That makes the val-ness of such properties vanish, because no code can ever assume any immutability on them, so we decided to take this feature back: from now on, only vars can be marked lateinit. We’ll keep thinking on use cases and improvements of this feature.

Backing fields and custom setters

An addition to the previously announced change in backing field syntax: if a property has a custom setter or is open, the setter may be reading the field before writing it, so there’s no syntax for initializing this property for the first time, unless it’s done upon declaration. So, we now require initializers for such properties:

var foo: Foo? = makeFoo() // initializer required
    set(v) {
        if (field != null)
            notifyListeners()
        field = v
    }

If we really need to initialize such a property in the constructor, we’d have to introduce a backing property:

private var _foo: Foo?
var foo: Foo?
    get() = _foo
    set(v) {
        if (_foo != null)
            notifyListeners()
        _foo = v
    }

init {
    _foo = ...
}

Type parameter declarations

Historically, Kotlin had two forms of syntax for type parameter declarations for functions:

fun <T> T.foo() = ...

fun T.foo<T>() = ...

Two is too many, so we decided to keep only the first one, because it places the declaration of T before its usages, which is easier to read and code completion will be more helpful.

Visibilities of subclasses and elements of declarations

It’s a technical requirement, but rather intuitive: if something is public, it should not, for example, expose a private type:

private open class Super

class Public : Super() {
    private class Private

    fun public(p: Private): Private = ...
}

From now on, public classes can’t extend private ones, and public members can’t expose private types.

More formally: a supertype or an element of a declaration must be (effectively) at least as visible as the declaration/class itself.

Marking result expressions

This one is not really decided, and very debatable indeed, but it can’t be added after 1.0, so we have been thinking about it for some time:

As some of you rightfully observed, it may be difficult at times to see what expressions are used as results of blocks or lambdas.

We are considering prefixing such result expressions with the ^ symbol (or maybe some other prefix) to make them visible, in the following cases:

  • expression is a result of a multi-line block or lambda, AND
  • its type is not Unit, nor Nothing.

Example (from the Kotlin code base):

val getter = target.getGetter() ?: run {
    val defaultGetter = DescriptorFactory.createDefaultGetter(target, Annotations.EMPTY)
    defaultGetter.initialize(target.getType())
    ^defaultGetter
}

or

 private fun transformTryCatchBlocks(methodNode: MethodNode, newTryStartLabels: HashMap<LabelNode, LabelNode>) {
     methodNode.tryCatchBlocks = methodNode.tryCatchBlocks.map { tcb ->
         val newTryStartLabel = newTryStartLabels[tcb.start]
         ^if (newTryStartLabel == null)
             tcb
         else
             TryCatchBlockNode(newTryStartLabel, tcb.end, tcb.handler, tcb.type)
     }
 }

Note that simply highlighting result expressions in the IDE is not really a solution:

  • It does not prevent accidentally changing results of lambdas or block by adding a harmless-looking println() at the end,
  • It doesn’t work on GitHub, in Terminal or anywhere outside the IDE

We did a quick experiment on the Kotlin codebase, and added all the necessary prefixes:

See diff on github

The diff above will give you a sense of what such code might look like.

To give you an idea of how often this will occur in the code: we got 493 lines changed out of about 230’000 lines of Kotlin code (0.21%), and 233 files were changed out of 2190 Kotlin files we have in our code base (every 10th file).

Comments below can no longer be edited.

58 Responses to Call for Feedback: Java Statics, Result Expressions and More

  1. msgile@gmail.com says:

    September 25, 2015

    = Backing Field Synthetic Name =
    I see the niceness of synthetic name of the field, like “this” or “it” but for some reason it feels strange to add another special case here.

    = Result Expressions =
    “^” is pretty subtle, does not match its use, and I am not seeing the intended symbolism (“Hey lookup?”). How about either a keyword like “yield” or “emit” or a symbol matching closures like “<-“. I hope this is optional, so I only have to use it if I think my code needs it, for safety or clarity.

    • Andrey Breslav says:

      September 25, 2015

      this is very different from it and field, because this a keyword, and the other two are simple automatically defined variables.

      ^ symbolizes an arrow pointing up. Being optional, it’s a lot less useful than if mandatory

      • Vladimir says:

        September 25, 2015

        Andrey, until you explain ^ as “arrow up” I think only about power or XOR). Why not use plain old return in such cases? Restrict user to place it always in places of this type. IDE will help to eliminate discomfort from writing looong word. And as you mention – this is very rare case – there will be no big problem without IDE and good compiler error message.

        • Andrey Breslav says:

          September 25, 2015

          return already has a meaning of “stop the execution of the current function, and return a value”, which is not the intended meaning here.

          • Dmitry Zaytsev says:

            September 26, 2015

            Why not? Isn’t it exactly how it reads: “Execution ends here and X is the result” – same as ^ symbol, if I got it right.

            I am looking at it from the perspective of Anonymous classes in Java, which are, despite being a different thing, was often used as substitution for lambdas. Imagine I have Java class (Java 7) which uses some functional interfaces. I would have to use return, since it’s Java. Now, when I will migrate such class to Kotlin it would totally makes sense for me if return statement will stay as is for multi-line blocks.

            • Andrey Breslav says:

              September 27, 2015

              I don’t think your analogy is correct: anonymous classes (both in Java and Kotlin) have explicit function declarations inside, so there’s no confusion as of where a return is returning from. With lambdas that may look exactly like blocks, it is often unclear.

    • Christian says:

      September 25, 2015

      yield or emit sounds good IMO, but why not simply “return”? After all, the expression does return something.

      • Andrey Breslav says:

        September 25, 2015

        return means more than just “mark last expression”, it alters control flow

        • claudiu says:

          September 25, 2015

          ^ looks bad. yield or emit more more better.

          please don’t make it scala πŸ™‚

        • Dmitry Zaytsev says:

          September 26, 2015

          This is also a question whether ^ should affect control flow or not. I already see (in my head) code which uses several ^ expressions within a same file without stopping the execution, which then would be arguably harder to follow.

          • Dmitry Zaytsev says:

            September 26, 2015

            Sorry, within a same block, not file

    • Sergey Igushkin says:

      September 25, 2015

      +1 for the result expression marks being optional. Maybe IDE should just suggest marking a result expression explicitly if the construct becomes too complex and may be ambiguous.
      But if they will be mandatory, then IMO tooling must mark an expression as a result one automatically when having typed a code block and provide a way to move the mark to a new expression below in a few keystrokes without editing too much.

  2. Christian says:

    September 25, 2015

    Please don’t use the “^” prefix, but another one. On the German keyboard for example, pressing ^ on its own does nothing, pressing it twice results in “^^”.

    • Andrey Breslav says:

      September 25, 2015

      My condolences to those Germans who have to manipulate bits in C/C++ or write manuals with Mac OS X shortcuts in them πŸ™‚

      Jokes aside, I see your point.

    • Jayson says:

      September 25, 2015

      How is <- on german keyboards?

    • Alexander Kosenkov says:

      October 6, 2015

      Press ^ and then a space bar

  3. Chris Kent says:

    September 25, 2015

    Result Expressions

    I’m not in favour of this. If your code needs this isn’t it a sign that you should probably refactor it and move some of your logic out into a function? I find code that uses lambdas is much more readable when the lambdas are small.

    Other languages with similar semantics manage without this, e.g. Ruby, Scala. It’s also pretty ugly.

    • Jayson says:

      September 25, 2015

      It is not just about readability. It allows the compiler to check that you have not made an error, or have an accidental change you didn’t intend. It would be like implying all return and hoping you didn’t make a mistake in your code that is only verified by readability. We would never drop return yet we imply something similar by implying emit or ^ or <- Why one, but not the other?

      • Chris Kent says:

        September 26, 2015

        I’m not actually bothered about return either. In Ruby and Scala return is optional in function definitions as well as in lambdas. I like that approach and was disappointed Kotlin didn’t adopt it. I’ve never had an issue with it producing code that was hard to read or prone to bugs.

        That’s why I’m not in favour of this change; it adds a bit of extra complexity and ugliness to the language to solve a problem that I don’t have. And if I did have it I’d solve it with some simple refactoring that would improve my code anyway.

    • Mohammad Shamsi says:

      September 26, 2015

      Agree with Chris.
      I also don’t see the need for “marking result expression”. I think it makes the code uglier and more complex.

      If the code is too complicated to understand, it deserves to be extracted to one or more separate methods.

  4. Rodrigo Quesada says:

    September 25, 2015

    “More formally: a supertype or an element of a declaration must be (effectively) at least as visible as the declaration/class itself.”

    Why is this a technical requirement? Isn’t defining a public class that extends a private/protected class perfectly valid code in Java? What are you gonna do with the Java interoperability requirement for this cases then?

    “…from now on, only vars can be marked lateinit. We’ll keep thinking on use cases and improvements of this feature.”

    Are you planning to provide an immutable-like property delegate, so that we can use it with vars then?

    • Andrey Breslav says:

      September 25, 2015

      Isn’t defining a public class that extends a private/protected class perfectly valid code in Java?

      It is.

      What are you gonna do with the Java interoperability requirement for this cases then?

      I don’t see an issue here.

      Are you planning to provide an immutable-like property delegate, so that we can use it with vars then?

      You mean something like assign-once? Well, it’s an option.

      • Rodrigo Quesada says:

        September 25, 2015

        It is.
        Then why is it a technical requirement in Kotlin? (just wondering)
        I don’t see an issue here.
        How would you extend a nested protected abstract static class defined in Java from Kotlin? (not very common, but it happens)
        You mean something like assign-once? Well, it’s an option.
        Yes. πŸ™‚

        Commenting further on the first subject, sometimes you simply don’t want to expose a base class that you only define internally for reusing purposes, so you might want it to be private (or protected).

        PS: I know the use cases for this are not so common, so you could probably reply me with some statistics from you code base, but the thing is they do exist and have a justified reason to do so. πŸ™‚

        • Rodrigo Quesada says:

          September 26, 2015

          Hey, just to remark, when I said “How would you extend a nested protected abstract static class defined in Java from Kotlin?”, I meant to said “How would you extend a nested protected abstract static class defined in Java from Kotlin, exposing the subclass as public?”.

        • Andrey Breslav says:

          September 27, 2015

          Then why is it a technical requirement in Kotlin? (just wondering)

          Because it makes things easier to implement, check and enforce.

          How would you extend a nested protected abstract static class defined in Java from Kotlin? (not very common, but it happens)

          I would just extend it. I can’t extend it with a public class, but a use case for such a situation is yet to be presented.

          Commenting further on the first subject, sometimes you simply don’t want to expose a base class that you only define internally for reusing purposes, so you might want it to be private (or protected).

          If it turns out that this limitation actually makes people’s lives harder, we can lift it. C# people have been living with these limitations for ages, and no one seems to care.

          • Rodrigo Quesada says:

            September 29, 2015

            Because it makes things easier to implement, check and enforce.

            I see, I guess that’s a good reason for taking that decision then, everybody wants you to release 1.0 soon. πŸ™‚

  5. Salomon BRYS says:

    September 25, 2015

    === Java Statics and Inheritance ===
    OK, Great πŸ˜‰

    === Lateinit val’s ===
    I’m very glad πŸ˜€

    === Backing fields and custom setters ===
    OK

    === Type parameter declarations ===
    I didn’t even know the second syntax existed :p

    === Visibilities of subclasses and elements of declarations ===
    Makes sense πŸ˜‰
    What about an overriden method that returns a private subclass that extends the type returned by the original method ?

    public interface Person
    
    internal class MalePerson: Person
    
    public interface Peoples {
        fun getAPerson(): Person
    }
    
    public class MalePeoples {
        override fun getAPerson(): MalePerson {}
    }
    

    === Marking result expressions ===
    Great, it would make the code more readable.
    I agree with msgile when he says that ^ is not expressive enough. I don’t think yield is a great choice because it is not the semantic of yield in many other languages that uses this keyword. I do very much like <- .

    • Salomon BRYS says:

      September 25, 2015

      Of course, MalePeoples extends Peoples (sorry) :

      public interface Person
      
      internal class MalePerson : Person
      
      public interface Peoples {
          fun getAPerson(): Person
      }
      
      public class MalePeoples : Peoples {
          override fun getAPerson(): MalePerson {}
      }
      
    • Andrey Breslav says:

      September 27, 2015

      What about an overriden method that returns a private subclass that extends the type returned by the original method ?

      If the type is less visible than the method, it is not allowed.

  6. Michel says:

    September 25, 2015

    Why not simply use “=” ?
    “=” makes more sense than “^” for marking result expressions.

  7. Dale King says:

    September 25, 2015

    Definitely don’t like the ^, it is giving me bad flash backs to lambdas in Objective-C

  8. Igor Lukanin says:

    September 25, 2015

    Marking result expressions with ^ looks extremely ugly and unintuitive. Please stick to some smart keyword. ‘return’ is ok in this case.

  9. Amal Samally says:

    September 26, 2015

    Backing property is a real sadness. I was hoping that in Kotlin we could avoid such ugly code (with different types for getter/setter & field).

    And I liked backing field syntax very much. I’ll miss it.

    +1 for the result expression marks being optional.

  10. Sergey Igushkin says:

    September 27, 2015

    I was wondering what is the reason for backing property dollar sign syntax removal other than string interpolation incompatibility? That one would be solved by just using $$property construct as it seems to me at first glance.

    • Andrey Breslav says:

      September 27, 2015

      Another reason is that it’s just too much syntax for use cases that are extremely rare: about 10 (ten) of them in a >1MLOC github corpus of Kotlin

  11. Dario Elyasy says:

    September 27, 2015

    Marking result expressions:
    Very useful indeed!
    Why the choice of a prefixed symbol, instead of a separate symbol/word at the beginning of the line? Wouldn’t that (a separate symbol/word) make it easier to searching/editing those marks in the IDE?

  12. Dario Elyasy says:

    September 27, 2015

    Marking result expressions:
    also if you make it mandatory, I see the point of all the people that complain about. I am in favor of making it mandatory, but you really need help from the IDE in order not to make this syntax annoying

  13. Alexander Eliseyev says:

    September 27, 2015

    I really like the idea of marking result expressions, it would make code more readable, especially in big lambdas. Although I’m not sure about ^, <- would look more appropriate here.

  14. Sven Jacobs says:

    September 27, 2015

    lateinit val

    While I understand the reason for this change I found lateinit val very convenient because I knew that these fields were always set through Dagger dependency injection and I didn’t have to perform null-safety checks on them. Does this mean that I now have to write someDependency!!.doSomething() again for all lateinit var fields?

    • Sven Jacobs says:

      September 27, 2015

      Couldn’t you allow lateinit val with a special compiler flag for people who know what they are doing? πŸ˜‰

      • Andrey Breslav says:

        September 27, 2015

        You can always fork the compiler if you want it really badly πŸ™‚ Otherwise our policy is: have a language feature for all users or don’t have it at all. Flags are for fine-tuning more low-level things.

    • Andrey Breslav says:

      September 27, 2015

      Yes, it does, and no, you couldn’t be sure it was necessarily Dagger changing them: any Java code could.

  15. Sven Jacobs says:

    September 27, 2015

    Result expressions

    The ^ syntax is not very beautiful πŸ™ Please keep it as it is or at least make it optional. Just using the return keyword would also be ok for me. But regardless what keyword you use, please make this one optional.

  16. Jacob Zimmerman says:

    September 27, 2015

    I agree with all but the last thing. For the expressions in a lambda, if it’s more than one line, the return statement should be there to make it clear. This is how java does their lambdas and I think it’s clearest. I’m not sure how it should be done in other expressions (if, when, try), but there’s no good reason to not use return after multiple lines in a lambda.

    • Andrey Breslav says:

      September 27, 2015

      There is: a lambda looks no different from a block in if/try/when, and there are many functions like with whose semantics are block-like too; so, we’d have to require a return in if if we required it in a multiline lambda.

      • Jacob Zimmerman says:

        September 28, 2015

        Huh. Never thought about that…

        Well, if you’re going to use a symbol, I’d highly prefer = or :, if possible. I know they’re used elsewhere, so maybe it’s not possible without potentially confusing the compiler.
        “out” could also potentially be a decent keyword as it’s already reserved (at least for some uses), it’s short, and it lives up to the meaning pretty well.

  17. Oliver Plohmann says:

    September 28, 2015

    As what the issue with β€œ^” is concerned I would prefer to simply go with “return”. As a former Smalltalk developer I like the Smalltalk-style β€œ^” return character, but I would nevertheless prefer a solution that is transparent for the developer. Is there a need the developer needs to be aware that a sliglty different case applies? If not, free the developer from having to deal with it. I would suggest not to go with yield. It always reminds me of Thread.yield. Also in several other languages yield means to give up the CPU voluntarily. In that way I also found for..yield in Scala confusing at first sight.

  18. Peter Niederwieser says:

    September 29, 2015

    ^ feels obscure and not in line with Kotlin’s philosophy. If you think this feature is necessary, you should pay the (keyword) price. With return being mandatory for multi-line methods, I wonder why a return(-like) keyword would be considered too heavy-weight for a multi-line lambda.

    Placing generic type arguments after “fun” is inconsistent with type declarations and function invocations. That’s why I always preferred placing them after the function name. Extension functions with generic receiver (as used in the code example) don’t seem like a good justification; I believe most users will never write such functions.

    • Jacob Zimmerman says:

      September 29, 2015

      Actually, such functions are the primary reason that I write extension functions. If you make a type that is contravariant but there’s some sort of composition function that requires covariance, the only way to gave that function is as a separate finction, whether extension or not. Usually the extension version is much more fluent. This example has come up in multiple libraries I’ve worked on and seen.
      Also placing it after fun is more congruent with how it’s done in Java. Obviously, we don’t necessarily want it to be like Java, but I was able to guess that syntax without ever having to look it up because it was natural to me.

  19. Chris Kent says:

    September 30, 2015

    = Inheritance of statics=

    It’s important to note that Java’s handling of statics is inconsistent with itself. Static methods defined on classes can be invoked in subtypes without the class name. But static methods defined in interfaces (introduced in Java 8) must be always be qualified with the name of the defining interface:

    interface Foo {
        static void interfaceMethod() {
            ...
        }
    }
    
    class Bar {
        static void classMethod() {
            ...
        }
    }
    
    class Baz extends Bar implements Foo {
        public static void main(String[] args) {
            classMethod(); // legal
            interfaceMethod(); // not legal
            Foo.interfaceMethod(); // legal
        }
    }
    

    Is Kotlin going to inherit this inconsistency from Java? Or is it going to allow interface methods to be invoked without the class name?

    I think it would be better to require the class name when calling static methods form supertypes. If you want to invoke the method without using the class name it is possible to explicitly import the method anyway.

    • Andrey Breslav says:

      September 30, 2015

      We are currently discussing the options regarding superinterfaces. Our user feedback shows that requiring qualified usages of statics is an annoying obstacle for Java interop because of how some popular libraries are designed

      • Chris Kent says:

        September 30, 2015

        But can’t that easily be fixed with an import? I use a lot of static methods every day, e.g. Collectors.toList(), Assert.assertEquals() and Mockito.mock(). Those methods are almost always statically imported. IDEs make that extremely easy.

        What’s the problem with importing a static method you want to use without qualifying it? Is it worth adding complexity to the language to avoid a few imports? Then the rules for invoking static methods would be extremely simple and consistent.

        • Andrey Breslav says:

          October 1, 2015

          Judging by the user feedback, we decided that it is worth it.

          • Chris Kent says:

            October 1, 2015

            Just out of interest, which libraries are we talking about?

  20. Dirk Dittert says:

    October 3, 2015

    Visibility of subclasses:
    Not sure what this is about and why it absolutely has to be prohibited. Shouldn’t it be possible to have a shared base class with internal functionality but return a number of derived public classes from functions?

    Result expressions:
    PLEASE PLEASE don’t use ^. <- is not really better. It would be really nice if Kotlin did not end up with Scala’s mess of random characters with magic meanings. Why not have a proper keyword like emit or supply for that. It could be optional for short statements (like if it’s the only statement in that scope). I never understood, why it is considered to be more readably to just write the name of a variable to return its value. Especially, as it only has that meaning if it really is the last statement in the control flow.

    Statics:/@Chris Kent:
    This is my main pain point in Kotlin because I’m doing a lot of TDD. Right now, it’s just not enjoyable to use popular libraries like AssertJ:
    There are assertThat methods that are in org.assertj.core.api.StrictAssertions, others come from org.assertj.core.api.Assertions (Assertions is derived from StrictAssertions).

    You can only import assertThat statically from one class. You always have to prefix the other one with the correct class. What makes it even worse: you have to know exactly which types are passed to the assertThat() static method. This is because the type of the first parameter determines whether you’re using assertThat from the base class or from the derived class.

    This is ugly and a pain while coding, especially because compiler errors for this aren’t really pointing you in the right direction.

    Please note: Even if that behavior stays that way, it would be a major relieve if the compiler searched in base classes for suitable static methods to print a helpful error message (something like “static method xy in inheritance hierarchy is not accessable through class abc in Kotlin.”).

    I know that static is a Java concept and not really a Kotlin concept. Even if you built a Kotlin facade for that one library, you would only have a solution for one particular case. Sounds like a lot of (unnecessary) busy work to me…

  21. Fred says:

    October 10, 2015

    really like what you guys are doing with Kotlin. The only two cents I’d like to offer is this. I remember watching a video of either Crockford or Stroustrup (can’t remember which) but they were saying how the biggest mistake Ritchie made when creating C was that he didn’t care whether a functions opening brace went at the end of the line or on the next line and so he allowed both.

    Thus he forever opened the door for opinions on the matter and devs have been going to war and complaining ever since. So please be decisive. Keep up the good work!

    • Andrey Breslav says:

      October 14, 2015

      I would love to live in the world where opinions on formatting are eliminated by compilers. However, we feel that building formatting style into a language would be too much of a threshold for adopters

  22. Vladimir says:

    October 31, 2015

    I think that ^ should be replaced by return. And anything after return should be error (unreachable code, or something like that).