Why not implement singleton pattern for the NULL object in Kotlin?

513 Views Asked by At
class Foo(val param1: String, var param2: Int) {
        // class body

        companion object {
                val NULL = Foo("", 0)
        }
}

Is the NULL object valid in Kotlin?

Although Kotlin allows adding default values in the constructor, is there a reason for NULL object to be discouraged, like in the above snippet?

Lets, say I don't need to allow constructing objects just with one param like: Foo("ABC") or Foo(5). (...after modifying the Foo to have default values in the constructor that is)

https://sourcemaking.com/design_patterns/null_object

3

There are 3 best solutions below

0
ardenit On

You can create a factory function without parameters that will return a null object:

class Foo(val param1: String, var param2: Int) {
        // class body
}

private val nullFoo = Foo("", 0)

fun Foo() = nullFoo
0
Tenfour04 On

Singleton isn't the right word for what you have there. A singleton is a class with only one instance allowed. This is a merely a globally accessible instance of a class that can have many instances.

I would advise against calling it NULL because of the meaning of the word null in Kotlin. Could be rather misleading. Maybe call it EMPTY.

Whether it's a bad practice is going to depend on how you're using it, but there's nothing inherently wrong with keeping some long-lived instances of classes.

I would however avoid doing this with a mutable class like this. It is implied that it is a constant, but in your example, someone could change the value of NULL.param2 from anywhere in an app and break code elsewhere that relies on its value being 0.

0
IlyaMuravjov On

A null object is a valid Kotlin design pattern.

When you use this pattern, you should create an interface and make both regular class(es) and a null object implement it. Also, the null object should be immutable like all other singletons.

Here is how you can implement the null object design pattern in Kotlin:

interface Foo {
    val param1: String
    val param2: Int
}

object NullFoo : Foo {
    override val param1: String get() = ""
    override val param2: Int get() = 0
}

class FooImpl(override val param1: String, override var param2: Int) : Foo

Alternatively, you can use actual null value and extension functions (properties) with nullable receivers:

class Foo(val param1: String, var param2: Int)

val Foo?.param1: String get() = this?.param1 ?: ""
val Foo?.param2: Int get() = this?.param2 ?: 0