3

I have a data class

data class MyModel(private val _data: MyData? = null)

And I want to ensure my data is only accessible when it is not null, else throw. I use the below which is good.

    fun getData(): MyData {
        return checkNotNull(_data) { "data shouldn't be null" }
    }

However, if I follow the guide as per Override getter for Kotlin data class, the below complaints I need to return MyData? instead of MyData

val data = _data
    get(): MyData {
        return checkNotNull(field) { "data shouldn't be null" }
    }

Is it true that field can't be cast to the Non-null version of it when return?

Lior Bar-On
  • 10,784
  • 5
  • 34
  • 46
Elye
  • 53,639
  • 54
  • 212
  • 474

3 Answers3

2

If your goal is to declare a getter for a Any? property that returns a Any, it's not possible. You'll get the following error:

Getter return type must be equal to the type of the property

So attempting to do something like

val test : String?
    get() : String = "hi"

Wouldn't work.


However, you could hide the nullable property and expose a non-nullable property which references the nullable value via casting:

private val test : String? = "hi"
val testNotNull : String = test as String

If test referenced null, an exception will be thrown.

For example:

fun main(args: Array<String>) = print(Demo().testNotNull)

class Demo(private var test: String? = "hi") {
    val testNotNull : String
.       get() = test as String
}

You can test this snippit out at try.kotlin.org

Although this is not safe. You should rethink your design. If you're not interoping with Java, you shouldn't punish yourself with nullable types.

Vince
  • 14,470
  • 7
  • 39
  • 84
  • For a normal variable, that's possible. For field, try "field as String", that is not helping. Do test out as a class variable custom getter, you'll see what I meant. – Elye Jul 11 '17 at 01:48
  • Hi @Vince, that's not `field`. That's just casting from another variable to it. But that's a nice trick as well. Upvote for you as well :) – Elye Jul 11 '17 at 03:02
  • @Elye Then it wouldn't be possible, since the type of the getter must match the type of the property. If the property is defined (or in your case, inferred) as `String?`, it's getter cannot be of type `String`. May I ask why you require this? It's seems unsafe, somewhat of a code smell. This could be a case of the [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) – Vince Jul 11 '17 at 03:09
  • Also note, if you want to throw a custom error, you can use something like `val data get() = _data ?: throw MyException()` – Ruckus T-Boom Jul 11 '17 at 15:57
1

I don’t think you can. What you did with the fun getData() is a valid approach IMO. Or you could just not use a data class and create a normal class, obviously.

What I think it may work is with something like this:

typealias notNullType = MyData

data class Test(private val _value: MyData? = null) {
    val v: notNullType = _value as notNullType
    get() { return field }
}

This would totally allow you to do:

fun play() {
    val t = Test(null)
    print(t.v) //see what I did? :-)
}

THAT BEING SAID… I don’t think “hiding” the ? optional is necessarily a good idea.

Martin Marconcini
  • 26,875
  • 19
  • 106
  • 144
  • 1
    Nice trick. But even by just `val t=Test(null)`, it will crash. Perhaps in such case, just get back to basic using `fun getData()` approach as I listed above. Give you an upvote though :) – Elye Jul 11 '17 at 02:38
  • Well, the problem is that you’re trying to hide the fact that a field is null. That breaks the promise of it being `?` to begin with, which, in turn, opens the door to the crash you mention. If you want “custom logic” in a `data` class, you’d have to add it (your `getData()` function is a good example), otherwise you’re trying to override the compiler’s check mechanisms and that’s what the `!` is for… (again, I wouldn’t) ;) – Martin Marconcini Jul 11 '17 at 02:40
  • I don't plan to hide the "?", but just crash it if it is null when accessing it, i.e. `checkNotNull(field) { "data shouldn't be null" }`. If it is not access, then it's alright, don't crash it. – Elye Jul 11 '17 at 02:43
0

It doesn't necessarily mean that the MyData class is null if you cast it like MyData?

The '?' Just allows the object to be null in the instance that it actually becomes null to avoid an exception at runtime.

You can make your class nullable and it can still contain your data.

ganjeii
  • 1,168
  • 1
  • 15
  • 26
  • The function that call getData() expect a non-null item. So we need to ensure it is not null. I don't want to propagate the ? all the way to the caller. Nor I want to use "!!". – Elye Jul 11 '17 at 01:47
  • Ahh I see I missed that sorry on my mobile – ganjeii Jul 11 '17 at 01:53