Upcoming Change: “Class Objects” Rethought

Kotlin M11 is coming very soon, and as some of you expressed concerns about being informed about the upcoming changes, I will describe one of the features of M11 and ask you for some feedback.

Class Objects: a short reminder

As you all know, any Kotlin class can have an associated class object:

Members of a class object are roughly analogous to static members of Java/C# classes, as they can be called on the class name:

(You can even use the [platformStatic] annotation to make those member actually static when seen from Java.)

In fact, Kotlin’s class objects and Java’s statics are not at all the same, because class objects are objects, i.e. they can extend classes, implement traits and serve as values at runtime:

Terminology Change

As you might have noticed, the term “class object” sounds a little ambiguous in English, and this is why many people tend to think that class object of Foo must be an instance (in other words, object) of Foo, which is totally not so. This, among other reasons, is why we are looking for another term and syntax. The current proposal has it as follows:

So, what used to be called “class objects” will now be called “default objects”.

More motivation is coming below, but at this point, please note how you feel about this change: is it better now? more confusing? about the same as before?
Please share your opinion in the comments below, now, before reading the motivation. Thanks a lot!

Why Default Objects

NOTE: all syntax presented here is provisional (we have it implemented, but might decide to change it before M11).

The unfortunate wording is not the only reason for this change. In fact, we redesigned the concept so that it is more uniform with normal objects.

Note that a class can (and always could) have many objects (usual, named singletons) nested into it:

Now, one of these objects may be declared with the default modifier, which means that its members can be accessed directly through class name, i.e. by default:

Accessing members of Obj1 requires qualification: KotlinClass.Obj1.foo(), for members of Obj2 the object name is optional: KotlinClass.foo().

One last step: the name of a default object can be omitted (the compiler will use the default name Default in this case):

Now you can still refer to its members though the name of the containing class: KotlinClass.foo(), or through full qualification: KotlinClass.Default.foo().

As you can see, unlike what we used to have with class objects, default objects are completely uniform with normal objects.

Another important benefit is that now every object has a name (again, Default is used when the name of a default object is omitted), which enables writing extension function for default objects:

This can be called as KotlinClass.bar(). This is how we implement platform-specific extensions for built-in classes like Int: e.g. Int.MAX_VALUE is an extension for Int.Default defined only on the JVM (JS ony has floating-point numbers, so Int.MAX_VALUE is meaningless there).

Summary

  • We are changing the syntax and conceptual load of what was formerly known as “class objects”: they are now default objects.
    • The old syntax will be deprecated and kept around for a while, so that you can migrate your code (in which the IDE will assist you).
  • The new concept is uniform with normal named objects.
  • You can now write extensions to default objects that can be called on class names.

Your feedback is very welcome! A large part of this change is about terminology and wording, so if you think the new concept is confusing is some way, please tell us in the comments.

About Andrey Breslav

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

43 Responses to Upcoming Change: “Class Objects” Rethought

  1. When I want to make sense of a Kotlin feature, I try to think of how it is likely to be implemented on the JVM. Although my initial reaction was negative (as an early Kotlin adopter, I’m used to the class object concept), the example with the objects Obj1 and Obj2 made perfect sense and doesn’t feel as “compiler magic” as class object did. It also better matches the general Kotlin philosophy, with simple conventions that bring useful code simplifications.

    Btw, +1 for more status updates like this post, it is indeed very hard for an outsider to follow Kotlin’s progress. Commit messages rarely provide useful insight, we need more exposure to the internal discussions that (I’m assuming) take place.

  2. Daniel Rothmaler says:

    Hm I think it’s better than before, but why do we need a Keyword at all?

    You could just drop it and simply specify the default object by name “object Default”… I like the “convention over configuration” principle. And Kotlin uses naming conventions in other places anyway (“it”, “get”, “set”).

    • Interesting proposal, thanks.

      P.S. “it”, “get” and “set” are not naming conventions, but Kotlin does use conventions for operators (like “plus” or “times).

      • I like Daniel’s proposal whereas “Default” should be highlighted in the IDE as a modifier/keyword to easily distinguish it from regular object names.

      • Daniel Rothmaler says:

        Well I meant the convention regarding the method names of property delegates. But “naming convention” might still be the wrong term.

        And also +1 from me, for the status update post :-)

        • You are right, I thought about the wrong “get” and “set”

          • Daniel Rothmaler says:

            BTW, a name based convention would also prevent us from weird things like this one:


            class KotlinClass {
            object Obj1 { ... }
            default object Obj2 { ... }
            object Default { ... }
            // Reader will wonder, which one is the default?
            ...
            }

            And on the consumer site:


            fun KotlinClass.Default.bar() { ... }

            fun main() {
            // Class consumer will wonder why this is not working (or does it???)
            KotlinClass.bar()
            }

            So I think you should never be able to rename the Default object. We could even completely omit the name, and leave it to the compiler, to name it “Default”.

            class KotlinClass {
            object Obj1 { ... }
            object { ... } // this one is the default
            ...
            }

            fun KotlinClass.Default.bar() { ... }

            But I think this is less intuitive, so I still would vote for an “object Default { … }” convention.

    • Julius Kunze says:

      +1 for “object Default” because it is easier to understand the whole nested object syntax at once and extending the object becomes intuitive and consistent.

    • After thinking of it, here’s why I don’t like this convention (and this is why we are discussing maybe requiring a special annotation on functions used as operators by convention):

      1. whenever you name a nested object Default, it becomes default, even if you don’t want it to, e.g. you meant the default implementation of an interface etc
      2. an object nested into another object can’t be default, but can be named Default, and this puts the user into an ambiguous situation, in which the compiler can’t help
  3. M Platvoet says:

    Great that you guys still reconsider implemented features to make Kotlin an even better language. Keep it up!

    I rather see the keyword(s) expres the purpose/consequences more. So I’d like to see it called shared (or singleton). It would probably be more in sync with the actual usage.

    I can image you could even allow these kind of keywords on class functions, so something like:

    class MyObject() {
    shared { // current class object
    }
    open shared fun foo() = 1 //part of the shared object
    }

    shared class MySingleton {}

    I’m not certain if this has conflicting meaning in other languages, that should of course be considered.

    • The crucial part of the current design is that “default objects” are very much like normal objects, and we would like to keep that.

      The syntax you propose drives us away from this goal. And I can’t guess what a proper meaning for “shared class” might be.

      • M Platvoet says:

        Yes, I like that design and I don’t think I’m proposing anything that diverts from that design. It should indeed just be like any other object. I just don’t like the naming of ‘object’. I think it’s confusing for newcomers and just readability in general. ‘default object’ can also have ambiguous meaning, e.g. a default instance of the current described class (just as ‘default T’ in C# refers to the default value).

        Of course, just as in any language, it’s the responsibility of the developer to understand the concepts in a specific language. And it really helps the language when it makes a bit sense as a natural language. Luckily you guys feel the same way because ‘class object’ doesn’t feel, for natural language reasons, like the best idea.

    • Cypressious says:

      IIRC Shared is the name of VB.NET’s equivalent of static.

  4. Andreas Sinz says:

    For me, “class object” implies, that this is the object of a class, so every instance of the class got this.

    On the other hand, if you skim through kotlin source code without proper syntax highlighting, you could easily mistake “class object” for an inner class,
    because of the same syntax (albeit class names should be upper case).

    Still I do like the new way to access the object, whether it will be “Foo.Default.bar()” or something else

    • I think nowadays we can assume a modern IDE for working with code and the times of editing code with Vi / Emacs are gone although I like Vi a lot for everything else and use it a lot.

      I can remember where static code analyzers like PMD & SonarQube highlighted (and still do) JUnit’s “assertEquals(expected, actual);” code snippets because assertEquals was a static method import and could be confused with a class-internal method (instance or static method). However IntelliJ highlights assertEquals in cursive font which makes it clear that it’s not a class-internal method. So SonarQube’s / PMD’s rule about static imports and using “Assert.assertEquals(expected, actual);” instead of the above is completely obsolete nowadays.

  5. As a newcomer to Kotlin, I’ve always seen class object as a weird way to do static but now I fully understand the power under this concept and the new name make it even clearer.

    And like Ioannis wrote, this kind of post is really useful 😀

  6. Rostislav says:

    Will objects have access to type information of enclosing class?

    https://gist.github.com/naixx/36807f263733609722cb

    +1 for naming convention object Default

    • So far this is not supported. There’s a rather simple way of working around this limitation: declare a supertype for your default object, and write an extension for it.

      • Rostislav says:

        If it is not a framework class which you cannot modify, thats a solution (and what I’m doing now) – define class object for every child class.
        But will it be possible to implement the proposal in future?

        Another question, will compiler generate default object if we want to write extension for Java object? (add platformStatic method or property to Java class)

        • It seems that your use case in general is to add static-like methods to a class (Java or Kotlin) that does not have a default object. In the Kotlin frame of mind, where there’re no static members, this is tricky to achieve: extensions need real instances to be invoked on, and if two extensions come from different third-parties, we can’t simply “generate” another instance for each of them, but there’s no way to coordinate…

  7. Very well and clearly explained! I had also problems to understand “class object” as object was leading me towards an instance and not a class. Also “class object” sounds to me contradictory, just like “dry water”. I think “default object” reduces the ambiguity.

    …and after reading your motivation, “default” makes even more sense.

  8. Даниил Водопьян says:

    Speaking on the name:

    default object looks for me as something that we have “by default” when the constructor is called without parenthesis: val obj = KotlinClass. So it implies that default object is an instance of the same class, which is what we are trying to avoid.

    In my opinion class object was absolutely fine since it represents an object in memory which hold class methods. it would be a “metaclass instance”, so my sugestion would be meta object. Also it may be an annotation as well.

    On the other hand when you explain this concept to someone, you usually say that this is an object, having the same name as the class. And we may call it mate object, fellow object or even associated object

    Going straight to the point, my proposal is meta object {...}, because it agrees to other languages (objective C, Python, C++)

    • Даниил Водопьян says:

      Ha, I just read the second part. Good thinking!
      Yep, if we may have several class objects, the default is definitely better.

    • The thing is that it is not really a meta-class, it contains nothing about the containing class, it is simply a convention to call methods on the class name.

    • Eugen says:

      +1. Your naming proposal is the most coherent to me.

      Another idea I had was to use the class name as the name for the “default”/”class”/”meta” object (similar to how constructors are named in Java) but I find a keyword approach better (less repetition).

      And please, more blog posts like these :).

  9. Ladislav Thon says:

    “Class object” was indeed a slightly unfortunate name. “Default object” is a bit better, especially with this explanation, but I like Scala’s naming here even more: “companion object”. To me, it’s really clear that a class’s companion object doesn’t have to be an instance of that class (and especially that it isn’t somehow a “default” instance of that class), and also that it isn’t a metaclass. Just a companion. I’m not suggesting a syntax here, just naming.

  10. Maarten says:

    With the caveat that I haven’t used Kotlin except to play with it a very little…

    I don’t find ‘default object’ to be better than ‘class object’. Default object sounds like something that you use when you don’t have a real object (instance object) available.

    I find the Scala term of ‘companion object’ to be much clearer, and consistent naming of such things can only help people cross from one language to another. Also, their structure of defining this object outside of the class definition makes it clearer that it is it’s own separate thing. Though I have to admit that the scoping rules can get a little more complicated by that construction.

    I’ll go read the second half of the blog entry now.

    • Maarten says:

      Having read the motivation now, that reasoning does make sense of both the structure and naming of the construct.

      However, I am concerned that this explanation may be needed before the terminology makes sense to someone, and it would be much preferable to have a construct and terminology that conveys the meaning without such an explanation.

      Especially since I think that the desire for writing ‘class methods’ on a class could be a lot more common than having multiple singleton objects nested inside a class.

  11. Ilya says:

    An object could have nested objects inside it:


    object Singleton {
    fun foo() = 42

    }

    Would an object allow the default object inside? 😉

  12. Mohammad Shamsi says:

    To me, the “default object” sounds better than current approach.
    But what if someone define an object inside class with the name Default but forget the “default” keyword? This might be slightly error prone.

    Like others, I like the idea of sharing and more transparency in kotlin development process.

  13. Даниил Водопьян says:

    I just thought that the convention for the class object should be not object Default, but rather object _class_name_ :

    In this way it makes perfect sense why you call this object via the class name and why you need a special key word.

    • Daniel Rothmaler says:

      Yes, I also thought about that. It would be very similar, to the Scala companion object.

      But what I don’t like, is the redundancy. It would make it harder to rename the class, as you could forget to rename the object also. The same applies, if you cut/copy & paste the code to another class.

      And last but not least it an extension function declaration would look ugly:
      fun User.User.bar() { ... }

      PS: How did you manage to get the code highlighted in your comment?

      • Даниил Водопьян says:

        @Daniel, this redundancy is exactly the motivation for using a special keyword like default.

        Also your comment on the ugly type declarations seems to be very important for any naming we take.

        I tried to add an extension function to a class object in kotlin-demo, and could not find a way.
        @Andrey, are such extension supported ?

        P.S. To make something highlighted put it with four space indent

  14. Alfie Kirkpatrick says:

    I agree in general with Daniel Rothmaler’s comments. I find ‘default object’ confusing (esp. for new users). Someone else pointed out it has connotations of a default state for the containing class. I prefer ‘companion object’ if a special keyword is needed at all.

    One thing struck me reading this and the docs on object in general. Is there any practical difference between:

    class Foo {
    val Default = object { … }
    }

    and

    class Foo {
    object Default { … }
    }

    • The difference is the ability to refer to the type by name, which is crucial when we need to write extensions to objects

      • Даниил Водопьян says:

        Does Kotlin have extensions to class objects at all ?
        If yes, then what is the syntax?

        • Kotlin did not have extensions to class objects. This is one of the reasons why we are changing the syntax here. As the post mentions, to write an extension to a default object, you can use its name:

  15. Guangyu HE says:

    I think that default object is more confusing than ‘class object’

  16. Scott says:

    Thank you for the explanation. It was very clear, and I have a much better understanding of it now.

    I do agree with some of the other posters though in that ‘default’ isn’t much better than ‘class’ (although it is better).

    I disagree with the posters who wanted to get rid of the keyword ‘default’ in favor of just naming the object ‘Default’. A special pairing is happening, and it wouldn’t be obvious that anything at all was happening without a keyword being used. That would essentially boil down to pure compiler magic, which I feel is a step in the wrong direction.

    I understand the difficulty in coming up with a keyword, as I’m trying to think of something better than ‘default’. ‘default’ isn’t terrible, but perhaps something like ‘paired’, or ‘companion’ would be more intuitive.

    Either way +1 for Roadmap, and more updates. Don’t give me ETAs. Just tell me planned features for new releases, and I’ll be a ton happier.

  17. ruslan says:

    The feature is inspired by companion object in Scala so why don’t call it so?

  18. Dirk Dittert says:

    I believe there should be only one way to declare the default object. Thus, only one of those should be valid syntax:

    object Default { }

    default object {}

    In the second case, it could still have the name Default (that could then be used to declare extension functions to the default object).

    The first approach is more consistent when extension functions are declared. The second is more consistent with “marking the default object through syntax”

    I don’t like the name of the class for the default object:
    fun SomeClass.SomeClass.extensionFunc() is repetitive.

Comments are closed.