How to implement mutable optional in Kotlin? - kotlin

I want a class which is equivalent to Java Optional but also
Properly handles null value ("Not set" state is different from "Null set")
Is mutable
Uses Kotlin built-in null-safety, type parameter can be either nullable or non-nullable which affects all methods.
Non-working code:
class MutableOptional<T> {
private var value: T? = null
private var isSet: Boolean = false
fun set(value: T)
{
this.value = value
isSet = true
}
fun unset()
{
isSet = false
value = null
}
fun get(): T
{
if (!isSet) {
throw Error("Value not set")
}
return value!! // <<< NPE here
}
}
fun f()
{
val opt = MutableOptional<Int?>()
opt.set(null)
assertNull(opt.get())
}
The problem is that if I try to set null, get() call fails with null pointer exception (caused by !! operator).
Some not-working proposals:
Do not use members of type "T?" in such class. I would not use it if I knew how to leave them uninitialized (not allowed by the compiler) or how to make them to have default initialization.
Use "fun get(): T?" (with nullable result). I want the result type to have the same nullability as the class type parameter. Otherwise there is no meaning in such null-safety if it is lost in a simple generic class, and I will need to set !! manually where I am sure it is non-nullable (the thing the compiler should ensure), making my code looking like wedge-writing.
Note: This example is synthetic, I do not really need the mutable optional, it is just a simple and understandable example, illustrating a problem I encounter occasionally with Kotlin generics and null-safety. Finding solution to this particular example will help with many similar problems. Actually I have a solution for immutable version of this class but it involves making interface and two implementation classes for present and non-present values. Such immutable optional can be used as type of "value" member but I think it's quite big overhead (accounting also wrapper object creation for each set()) just to overcome the language constraints.

The compiler wants you to write code that will be type-safe for all possible T, both nullable and not-null (unless you specify a not-null upper bound for the type parameter, such as T : Any, but this is not what you need here).
If you store T? in a property, it is a different type from T in case of not-null type arguments, so you are not allowed to use T and T? interchangeably.
However, making an unchecked cast allows you to bypass the restriction and return the T? value as T. Unlike the not-null assertion (!!), the cast is not checked at runtime, and it won't fail when it encounters a null.
Change the get() function as follows:
fun get(): T {
if (!isSet) {
throw Error("Value not set")
}
#Suppress("unchecked_cast")
return value as T
}

I got a similar issue. My use case was to differentiate null and undefined value when I deserialize JSON object. So I create an immutable Optional that was able to handle null value. Here I share my solution:
interface Optional<out T> {
fun isDefined(): Boolean
fun isUndefined(): Boolean
fun get(): T
fun ifDefined(consumer: (T) -> Unit)
class Defined<out T>(private val value: T) : Optional<T> {
override fun isDefined() = true
override fun isUndefined() = false
override fun get() = this.value
override fun ifDefined(consumer: (T) -> Unit) = consumer(this.value)
}
object Undefined : Optional<Nothing> {
override fun isDefined() = false
override fun isUndefined() = true
override fun get() = throw NoSuchElementException("No value defined")
override fun ifDefined(consumer: (Nothing) -> Unit) {}
}
}
fun <T> Optional<T>.orElse(other: T): T = if (this.isDefined()) this.get() else other
The trick: the orElse method have to be defined as an extension to not break the covariance, because Kotlin does not support lower bound for now.
Then we can define a MutableOptional with no cast in the following way:
class MutableOptional<T> {
private var value: Optional<T> = Optional.Undefined
fun get() = value.get()
fun set(value: T) { this.value = Optional.Defined(value) }
fun unset() { this.value = Optional.Undefined }
}
I am happy with my immutable Optional implementation. But I am not very happy with MutableOptional: I dislike the previous solution based on casting (I dislike to cast). But my solution creates unnecessary boxing, it can be worst...

Related

Kotlin nullable generic

I am not understand why this code not working
class nullableGenericA<T: Any?>{
fun someMethod(v: T){}
fun someMethod(){
someMethod(null)
}
}
error: "Null can not be a value of a non-null type T".
How it works? If nullable is not part of type why works this
class NullableGenericB<T>(val list: ArrayList<T>){
fun add(obj: T){
list.add(obj)
}
}
fun testNullableGenericB(){
NullableGenericB<String?>(ArrayList()).add(null)
}
Your generic type is not necessarily nullable. It only has an upper bound of allowing nullable, but it is not constrained to be nullable. Since T could possibly be non-nullable, it is not safe to pass null as T. For example, someone could create an instance of your class with non-nullable type:
val nonNullableA = NullableGenericA<String>()
If you want to design it so you can always use nullables for the generic type, then you should use T? at the use sites where it is acceptable. Then, even if T is non-nullable, a nullable version of T is used at the function site.
class NullableGenericA<T>{
fun someMethod(v: T?) {}
fun someMethod() {
someMethod(null)
}
fun somethingThatReturnsNullableT(): T? {
return null
}
}

Why do i need to specify constructor argument as nullable generic type?

I'm trying to make OK() to call ApiResponse constructor. When I give null to ApiResponse constructor argument, it shows error that type mismatches.
If I change data type to T? it works. Why is it happening? Default upper bound of T is Any? so i thought it won't be any problem to assign null.
class ApiResponse<T> private constructor(
val data: T, // If I change data type to T?, no error
val message: String?
) {
companion object {
fun <T> OK(): ApiResponse<T> {
return ApiResponse(null, null)
}
fun <T> OK(data: T): ApiResponse<T> {
return ApiResponse(data, null)
}
}
}
I've searched with keywords kotlin, generic, constructor, nullable, T but i could not find answer.
In
fun <T> OK(): ApiResponse<T> {
return ApiResponse(null, null)
}
if someone calls ApiResponse.OK<String>(), then it tries to construct an ApiResponse where data is null and also of type String, which is incompatible. None of your types prevent that call -- when you have a generic type argument to the function like that, the caller can specify any T they please, including a nonnull type.
You must either return an ApiResponse<T?>, or not have an argumentless OK factory method.

Determine whether the reified type is nullable

Suppose I have a delegate class that needs a class type and a Boolean. I have specific functionality in mind if the type of the property this delegate is used for is nullable. To keep it simple, let's say it's supposed to throw an error for nulls depending on the Boolean parameter.
class Sample<T: Any> (val type: KClass<T>,
val allowNulls: Boolean){
private var value: T?
operator fun getValue(thisRef: Any, property: KProperty<*>): T? {
return if (allowNulls)
value
else
value?: throw Exception("Value is null!")
}
operator fun setValue(thisRef: Any, property: KProperty<*>, value: T?) {
this.value = value
}
}
I want to create a reified function for easily generating an instance of this class that automatically determines whether the type should be nullable. Again, this is useful for a delegate that behaves differently for nullable properties. This would for example be used to allow different behavior depending on whether delegated properties were nullable:
val nullableString by sample<String?>()
val nonnullString by sample<String>()
val nullableString2: String? by sample()
val nonnullString2: String by sample()
How can I determine if the reified type is nullable? I don't see a way to access this information:
inline fun <reified T: Any> sample(): Sample<T>{
return Sample(T::class, /** T is nullable */)
}
If T is a reified generic type parameter, you can find whether it's nullable or not with a simple, though not obvious at first sight check:
if (null is T) {
// T is nullable
}
However in your example T has Any upperbound, so the expression will always be false.
There's a very simple answer to this!  But first:
Remember that the top type in Kotlin is Any? (which is nullable).  The non-nullable Any is a subtype, and all non-nullable types descend from that.  So a type is nullable if it's not a subtype of Any.
So your generic <reified T: Any> is already restricting to non-nullable types, and your function could just use false!
However, if you relax that restriction, the test becomes just null is T — after all, a type is nullable iff it includes null as a value:
inline fun <reified T: Any?> sample(): Sample<T> {
return Sample(T::class, null is T)
}

Is it possible to make safe inline Optional in Kotlin?

In Kotlin sometimes I have to work with double nullability. For example, I need double nullability, when I want to use T? where T may be a nullable type. There are a few approaches for doing this:
Holder<T>? where Holder is data class Holder<out T>(val element: T) - example1
boolean flag variable - example1
containsKey for Map<K, T?> - example1
The special UNINITIALIZED_VALUE for representing the second kind of null - example1
The last approach has the best performance, but it's also the most error-prone. So I've decided to encapsulate it in inline class Optional<T>:
inline class Optional<out T> #Deprecated(
message = "Not type-safe, use factory method",
replaceWith = ReplaceWith("Optional.of(_value)")
) constructor(private val _value: Any?) {
val value: T?
get() =
#Suppress("UNCHECKED_CAST")
if (isPresent) _value as T
else null
val isPresent: Boolean
get() = _value != NULL
companion object {
#Suppress("DEPRECATION")
fun <T> of(value: T) = Optional<T>(value)
fun <T : Any> ofNullable(value: T?): Optional<T> =
if (value == null) EMPTY
else of(value)
#Suppress("DEPRECATION")
val EMPTY = Optional<Nothing>(NULL)
}
private object NULL
}
inline fun <T> Optional<T>.ifPresent(code: (T) -> Unit) {
#Suppress("UNCHECKED_CAST")
if (isPresent) return code(value as T)
}
inline fun <T> Optional<T>.or(code: () -> T): T {
ifPresent { return it }
return code()
}
The first problem with this Optional is public constructor, which allows creating instances with arguments of not matching type.
The second problem was noticed at testing time. Here is the failed test:
emptyOr { Optional.EMPTY }.value assertEql null
fun <T> emptyOr(other: () -> T): T = Optional.EMPTY.or(other)
Exception:
Exception ClassCastException: Optional$NULL cannot be cast to Optional
at (Optional.kt:42) // emptyOr { Optional.EMPTY }.value assertEql null
If I remove inline modifier from Optional, the test will pass.
Q: Is there any way to fix these problems without removing inline modifier from Optional?
1 Examples include some context. Please read them fully before writing that I added incorrect links.
I implemented exactly the same utility in one of my projects: OptionalValue.kt. My implementation is very similar to yours, it is also an inline/value class, so it should be cpu/memory efficient and it passes all tests I throw at it.
Regarding your first question: about a public constructor. There is an annotation specifically for this case: #PublishedApi. I tried to reproduce ClassCastException from your example, but it worked for me without problems, so I believe it was a bug in Kotlin itself (?).
Also, to answer the question why do we need double nullability, I explained my point here

Kotlin Generics and nullable Class type

How do I handle a nullable generics Class type in Kotlin?
Example function with generics:
fun <I> calculateStuff(valueType: Class<I>, defaultValue: I): I {
// do some work
return defaultValue;
}
Here is a calling function (note the 2nd param for calculateStuff(...))
fun doStuff() {
// works fine!
val myVar1 = calculateStuff(String::class.java, "")
// FAIL (null is not accepted... Error: "Cannot infer type parameter I in....")
val myVar2 = calculateStuff(String::class.java, null)
}
Work-around (change return type to I? AND defaultValue to I?):
fun <I> calculateStuff(valueType: Class<I>, defaultValue: I?): I? {
return defaultValue;
}
Preferred method, but does not seemed supported by Kotlin (note "String?::class.java"):
val myVar2 = calculateStuff(String?::class.java, null)
I really want to be able to send to the method (calculateStuff(...)) the return type, and if it can be null, as the first parameter... that way I ONLY have to null-check the return value if I pass a nullable Class in the first param.
Is this possible to do in Kotlin?
You need to change Class<I> to Class<out I>:
fun <I> calculateStuff(valueType: Class<out I>, defaultValue: I): I {
return defaultValue;
}
You can also do this using reified type parameters:
inline fun <reified I> calculateStuff(defaultValue: I): I {
// do some work
return defaultValue;
}
Usage:
val myVar1 = calculateStuff("") // myVar1 is String
val myVar2 = calculateStuff<String?>(null) // myVar2 is String?
Since there is no way to specify nullable classes as you discovered, your premise of limiting it by the first variable is not possible.
What is possible is to limit it by the nullability of the second variable by adding a second generic parameter:
fun <I, NI: I> calculateStuff(valueType: Class<NI>, defaultValue: I): I {
// do some work
return defaultValue;
}
val myVar2 = calculateStuff(String::class.java, null as String?) will now compile.
The reason this works is because in the kotlin type system, T is a subclass of T? so any non-nullable value is an acceptable value for a nullable type.