Call for Feedback: Java Statics, Result Expressions and More


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:

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:

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

Type parameter declarations

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

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:

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):

or

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).

About Andrey Breslav

Andrey is the lead language designer of Kotlin at JetBrains.
This entry was posted in Language design. Bookmark the permalink.

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

  1. msgile@gmail.com says:

    = 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.

    • 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:

        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.

        • 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:

            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.

            • 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:

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

    • Sergey Igushkin says:

      +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:

    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 “^^”.

  3. Chris Kent says:

    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:

      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:

        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:

      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:

    “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?

    • 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:

        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:

          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?”.

        • 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:

            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:

    === 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 ?

    === 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:

      Of course, MalePeoples extends Peoples (sorry) :

    • 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:

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

  7. Dale King says:

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

  8. Igor Lukanin says:

    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:

    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:

    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.

  11. Dario Elyasy says:

    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:

    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:

    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:

    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?

  15. Sven Jacobs says:

    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. 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.

    • 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.

      • 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:

    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:

    ^ 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.

    • 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:

    = 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:

    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.

    • 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:

        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.

  20. Dirk Dittert says:

    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:

    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!

    • 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:

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

Comments are closed.