TL; DR

Java developers new to Kotlin are often confused by the lack of static members. Which options Kotlin provides in order to replace the static keyword? Let's summarize for easy reference:

Java static… Replace with… void main() Top-level function Constants Top-level constants Methods in utility class Top-level functions Factory methods Companion object functions Private methods Private companion object functions or private top-level functions in same file Singleton accessors Object instance

Continue reading for the details.

Where is my precious static?

When Java developers start to code in Kotlin, one of the first question is: “How can I define a static function or property?". But there is no such keyword in Kotlin. Why?

Several modern languages dropped statics for the same reason they dropped explicit support for primitive types: simplify the language by removing special cases. As far as I know, Scala is the first JVM language that did that and Kotlin adopted a similar approach.

Java static methods and fields are attached to the class where they are declared, not its instances. Static member lives in a separate world than instance members, governed by different rules:

Instance methods Static methods Scope Instance Class Can override open methods in superclass Yes No (but can shadow them) Can implement methods in interface Yes No Dispatch Dynamic, by actual runtime type Static, by type known at compile-time

Having them inside the same language construct, the class , is mixing together two different concerns: on one side the dynamic behavior of individual instances and, on the other side, global static code. It's no surprise that beginners have a hard time understanding static members: first we explain them the object-oriented mantra that classes are like blueprints to define how individual objects can behave, but then we go on and tell them that the blueprints themselves can have non-object-oriented behaviors on their own…

In Kotlin, a class can only define the behavior of instances: it's really like a blueprint. Static or global functions are defined separately, outside of the class definitions.

Let's now look in detail at the options Kotlin provides.

Top-level functions & properties

In Java everything must be defined inside a class. Fortunately Kotlin allows to define top-level functions, outside any class definition, directly in a .kt file.

Function main()

Every Java beginner starts by writing this “Hello world” application.

1 2 3 4 5 public class HelloWorld { public static void main ( String [ ] args ) { System . out . println ( "Hello World" ) ; } }

There is a lot of boilerplate for such a simple program. With Kotlin top-level functions we can write the main() function with a minimal amount of ceremony.

1 2 3 4 // Look Ma! No class! fun main ( ) { // 'args: Array<String>' parameter is optional println ( "Hello World" ) }

Get rid of utility classes

I cannot count how many *Utils or *Helper classes I've created in Java during my career, with only static methods inside…

1 2 3 4 5 6 7 8 9 //Java public class StringUtils { private StringUtils ( ) { /* Forbid instantiation of utility class */ } public static int lowerCaseCount ( String value ) { return value . chars ( ) . reduce ( 0 , ( count , current ) - > count + ( Character . isLowerCase ( current ) ? 1 : 0 ) ) ; } }

Let's get rid of the utility class by rewriting that with a top-level function:

1 2 3 package toplevel fun lowerCaseCount ( value : String ) : Int = value . count { it . isLowerCase ( ) }

That was easy!

Top-level functions & properties are associated with their package. This also means we cannot declare 2 top-level functions or properties with the same name in different files of the same package.

We can access a top-level function from another package by importing it from the package where it was defined:

1 2 3 4 5 6 7 package anotherpackage import toplevel.lowerCaseCount fun main ( ) { println ( lowerCaseCount ( "Hello World" ) ) }

It's often a good idea to consider converting such top-level functions to extension functions, if they can be thought as extensions of one of the arguments.

1 2 3 4 5 fun String . lowerCaseCount ( ) : Int = this . count { it . isLowerCase ( ) } fun main ( ) { println ( "Hello World" . lowerCaseCount ( ) ) }

Constants

A property can also be declared top-level, but like in Java, we should use them only for constants or immutable values.

1 2 3 4 5 6 7 // This is OK const val CONSTANT_STRING = "CONSTANT" val READONLY_LIST = listOf ( "value1" , "value2" ) // NOT OK: avoid public mutable top-level properties var mutableValue = "currentValue" val mutableList = mutableListOf ( "value1" , "value2" )

Private top-level functions

In Java, when refactoring long methods, I like to extract small referentially transparent private methods. Referentially transparent functions are easier to reason about and refactor because they forbid side effects, i.e their output purely depend on their input arguments. To ensure they stay referentially transparent, I make them static any access to instance fields.

1 2 3 4 5 6 7 8 9 // Java public class MyClass { private String myString ; public void doSomething ( ) { int count = lowerCaseCount ( myString ) ; . . . } private static int lowerCaseCount ( String value ) { . . . } // private static pure function }

In Kotlin I extract them into private top-level functions instead (or in some cases into companion functions, see below). Private top-level functions and properties are visible only in the file declaring them.

1 2 3 4 5 6 7 8 class MyClass ( private val myString : String ) { fun doSomething ( ) { val count = myString . lowerCaseCount ( ) . . . } } private fun String . lowerCaseCount ( ) : Int = . . .

From Java

From Java, top-level members can be called like static members on a class named after the Kotlin file where the top-level function is declared, with the Kt suffix. Assuming we declared lowerCaseCount() in a file called TopLevel.kt , then in Java we'll call it like this:

1 System . out . println ( TopLevelKt . lowerCaseCount ( "Hello Java" ) ) ;

If you dislike the Kt suffix, you can rename this class generated by Kotlin using the annotation @file:JvmName :

1 2 3 4 @file : JvmName ( "TopLevel" ) package toplevel fun lowerCaseCount ( value : String ) : Int = . . .

Object singletons

Kotlin natively supports the Singleton pattern with Object declarations. Because it's an actual value, the Object can extend classes or interfaces and its functions are dynamically dispatched. Nonetheless they can be called very much like static members in Java.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface Counter { fun count ( value : String ) : Int } object LowerCaseCounter : Counter { // can implement an interface override fun count ( value : String ) = value . count { it . isLowerCase ( ) } } object UpperCaseCounter : Counter { // another implementation of the same interface override fun count ( value : String ) = value . count { it . isUpperCase ( ) } } fun main ( ) { // functions on singleton objects can be called like Java static methods println ( LowerCaseCounter . count ( "Hello World" ) ) // prints 8 println ( UpperCaseCounter . count ( "Hello World" ) ) // prints 2 // But singletons are values and can be assigned to variables and passed as arguments val counter = if ( someCondition ) LowerCaseCounter else UpperCaseCounter println ( counter . count ( "Hello Kotlin Everywhere" ) ) // dynamic dispatch }

From Java

From Java, by default, you can access Object members through the static INSTANCE field:

1 System . out . println ( LowerCaseCounter . INSTANCE . count ( "Hello World" ) ) ;

When an Object function is not overriding a function from a superclass or interface, which necessarily implies dynamic dispatch, then it can be made static by using the @JvmStatic annotation:

1 2 3 4 object StringOps { @JvmStatic fun lowerCaseCount ( value : String ) : Int = value . count { it . isLowerCase ( ) } }

The compiler now generates an actual static member which can be called directly from Java:

1 System . out . println ( StringOps . lowerCaseCount ( "Hello World" ) ) ;

Companion objects

Singleton objects are very nice but what if your Java class have static functions that access private non-static members? How to port that to Kotlin? Companion objects are your friends.

Static factory methods

Companion objects are singletons values that are declared inside a class and have a close relationship with it. Members of the companion object can be called using the name of the accompanied class. Moreover the companion object's members can access any private member of the class instances. Companion objects are therefore especially useful for factory methods.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Rocket private constructor ( ) { private fun attachPropellers ( ) { . . . } companion object { fun build ( ) : Rocket { val rocket = Rocket ( ) // can call private constructor rocket . attachPropellers ( ) // can call private function return rocket } } } fun main ( ) { val rocket = Rocket . build ( ) // Companion function called using the accompanied class name }

From Java

By default the companion object members are accessible from Java through the static Companion field:

1 Rocket rocket = Rocket . Companion . build ( ) ;

Again, we can use the @JvmStatic annotation to allow direct static access to the companion members from Java:

1 2 3 4 5 6 class Rocket private constructor ( ) { companion object { @JvmStatic fun build ( ) : Rocket = . . . } }

1 2 // Then in Java Rocket rocket = Rocket . build ( ) ;

Conclusion