Question
In Java, methods can be declared with the static keyword, but Kotlin does not have a static keyword.
What is the best way to represent a Java static method in Kotlin, and which Kotlin feature should be used in different situations?
Short Answer
By the end of this page, you will understand how Kotlin handles functionality that would be written as static in Java. You will learn when to use top-level functions, companion objects, and object declarations, and how each option maps to common Java patterns.
Concept
Kotlin does not use the static keyword. Instead, it offers several language features that cover the same use cases more cleanly.
In Java, static is often used for:
- utility methods
- factory methods
- constants
- shared state
- methods tied to a class rather than an instance
In Kotlin, the equivalent depends on why the method is static.
1. Top-level functions
A function can be declared directly in a file, outside any class:
fun add(a: Int, b: Int): Int {
return a + b
}
This is the most common replacement for Java utility-style static methods. If the function does not need access to class internals, a top-level function is often the simplest and most idiomatic choice.
2. Companion objects
If the function conceptually belongs to a class, put it inside a companion object:
class User(val name: String) {
companion object {
fun createGuest(): User {
return User("Guest")
}
}
}
This is commonly used for factory methods and class-level behavior.
Mental Model
Think of Java static as saying: "this belongs to the class itself, not to a particular object."
Kotlin splits that idea into different tools:
- Top-level function = a tool placed directly on the workbench, ready to use
- Companion object = a tool stored inside a specific class's cabinet
- Object declaration = a special toolbox that exists only once
So instead of one static keyword doing many jobs, Kotlin gives you separate features for separate purposes.
That makes your intent clearer:
- Is this just a general utility? Use a top-level function.
- Is this part of how a class is created or managed? Use a companion object.
- Do you want one shared holder for behavior or state? Use an object declaration.
Syntax and Examples
Top-level function
fun greet(name: String): String {
return "Hello, $name"
}
fun main() {
println(greet("Ava"))
}
Use this when the function does not logically belong to a class.
Companion object
class Logger private constructor(val prefix: String) {
companion object {
fun default(): Logger {
return Logger("APP")
}
}
}
fun main() {
val logger = Logger.default()
println(logger.prefix)
}
Use this when the method is related to the class itself, such as a factory method.
Object declaration
object StringUtils {
: {
text.isBlank() || text.length <
}
}
{
println(StringUtils.isBlankOrShort())
}
Step by Step Execution
Consider this example:
class User(val name: String) {
companion object {
fun guest(): User {
return User("Guest")
}
}
}
fun main() {
val user = User.guest()
println(user.name)
}
Step by step:
class User(val name: String)defines a class with one property:name.- Inside the class,
companion objectdefines class-level behavior. fun guest(): Usercreates and returns a newUserobject.- In
main(),User.guest()is called. - Kotlin resolves that call through the companion object.
- The function returns
User("Guest"). - The returned object is stored in
user. println(user.name)prints .
Real World Use Cases
Utility functions
Top-level functions are commonly used for:
- formatting dates
- parsing strings
- validating input
- converting data
Example:
fun normalizeEmail(email: String): String {
return email.trim().lowercase()
}
Factory methods
Companion objects are useful when creating objects in controlled ways:
class ApiClient private constructor(val baseUrl: String) {
companion object {
fun production(): ApiClient {
return ApiClient("https://api.example.com")
}
}
}
Shared configuration or helpers
object declarations are useful for one shared holder:
object AppConfig {
const val API_VERSION = "v1"
}
Real Codebase Usage
In real Kotlin projects, developers usually choose based on ownership and intent.
Common patterns
Top-level utility functions
Used for pure operations that do not depend on object state.
fun parsePort(value: String): Int {
return value.toIntOrNull() ?: 8080
}
This is common in:
- validation helpers
- mapping functions
- formatting code
- extension-support utilities
Companion object for factories
Used when object creation should be controlled or named.
class Session private constructor(val token: String) {
companion object {
fun fromToken(token: String): Session {
require(token.isNotBlank())
return Session(token)
}
}
}
This often appears with:
- validation before construction
- named constructors
- parsing methods such as
fromJson()
Common Mistakes
1. Translating every Java static method into a companion object
Beginners often put everything into a companion object just because it feels closest to Java.
class MathUtils {
companion object {
fun add(a: Int, b: Int): Int = a + b
}
}
This works, but if the function is just a general helper, a top-level function is usually cleaner:
fun add(a: Int, b: Int): Int = a + b
2. Using an object when no singleton is needed
If you only want a simple helper function, an object may add unnecessary structure.
Broken design:
object EmailHelper {
fun normalize(email: String): String = email.trim().lowercase()
}
Often simpler:
Comparisons
| Kotlin feature | Best use case | Similar to Java static? | Notes |
|---|---|---|---|
| Top-level function | General utility functions | Yes, often | Most idiomatic for standalone helpers |
companion object | Class-related methods | Yes | Good for factory methods and class-level logic |
object declaration | Singleton container | Sometimes | Useful for shared state or grouped behavior |
@JvmStatic | Java-friendly access | Yes | Mainly for interoperability with Java |
Top-level function vs companion object
| Choice |
|---|
Cheat Sheet
Quick rules
- Kotlin has no
statickeyword. - Use a top-level function for standalone helpers.
- Use a companion object for class-related methods.
- Use an object declaration for a singleton.
- Use
@JvmStaticwhen Java interoperability requires static-style access.
Syntax
Top-level function
fun doWork() {
println("working")
}
Companion object
class MyClass {
companion object {
fun create(): MyClass = MyClass()
}
}
Object declaration
object Utils {
fun help() {}
}
Java-friendly static access
FAQ
What is the Kotlin equivalent of a Java static method?
Usually a top-level function or a method inside a companion object, depending on whether the method belongs to a class.
Does Kotlin support static methods?
Not with a static keyword. Kotlin uses other features to cover the same use cases.
When should I use a companion object in Kotlin?
Use it when the function is logically tied to a class, such as factory methods, constants, or class-level utilities.
Are top-level functions better than companion objects?
For standalone utility behavior, yes. They are often simpler and more idiomatic.
What is @JvmStatic in Kotlin?
@JvmStatic tells Kotlin to expose a method in a Java-friendly static style when used inside a companion object or object.
Can a companion object access instance properties?
No. A companion object does not automatically have access to instance data. It can only use class-level members or work with an instance passed to it.
Should I use object for utility methods?
Sometimes, but only when a singleton container makes sense. For many helpers, top-level functions are simpler.
How do I convert Java utility classes to Kotlin?
In many cases, convert their static methods into top-level functions. If grouping is useful, consider an .
Mini Project
Description
Build a small Kotlin program that models a Temperature class and demonstrates three ways to replace Java-style static methods: a top-level conversion function, a companion object factory method, and an object-based utility helper. This project helps you practice choosing the right Kotlin feature for the right kind of class-level behavior.
Goal
Create and use top-level, companion object, and object declaration methods in one Kotlin program.
Requirements
- Create a top-level function that converts Celsius to Fahrenheit.
- Create a
Temperatureclass with a companion object factory method. - Create an
objectutility that checks whether a temperature is freezing. - In
main, call all three and print the results. - Keep the code valid and idiomatic Kotlin.
Keep learning
Related questions
Android AlarmManager Example: Scheduling Tasks with AlarmManager
Learn how to use Android AlarmManager to schedule tasks, set alarms, and handle broadcasts with a simple beginner example.
Can You Extend a Data Class in Kotlin? Inheritance, Limits, and Better Alternatives
Learn why Kotlin data classes cannot be extended, what causes the component function clash, and which alternatives to use instead.
Difference Between List and Array in Kotlin
Learn the difference between List and Array in Kotlin, including mutability, size, APIs, performance, and when to use each one.