Null-safe(r) Java property access for properties with getFoo()+hasFoo() methods


#1

In some APIs, most prominently for me the Google Protobuf Java/Javalite generated APIs, a getter never returns null but rather always returns a default instance, while using a hasser to return whether or not the object should be considered.
My proposal is that for interop, this Java:

class Bar {
  String getFoo()
  boolean hasFoo()
}

Would be interpreted in Kotlin as:

class Bar {
  val foo : String?
}

instead of the current

class Bar {
  val foo : String  // Which, being Java interop will not do any null-checking.
}

This would make working with things like protobufs much less cumbersome. In Java you currently have to make large chains of has/get calls if you want to assure you are dealing with non-default data. This would enable Kotlin safe-call operators so bar?.foo?.baz would work.

Potential drawbacks:

  • Null safety is guaranteed because a null check is required to use the item at all, but in this case, depending on the API, the actual value may or may not be null (such as in the ‘return default instance case’). This means it’s somewhat of a special case, where the hasser returning false would override the literal nullness of the object in a bar.foo != null check.
  • It seems unlike but possible that someone would actually want to explicitly grab a default instance where hasFoo was returning false.
  • Potential thread safety issues for mutable data structures.
  • Would be backwards incompatible and break code that is accessing objects with hassers without using the nullable type.

Interested to hear thoughts on this. I think with a feature like this we could have pretty clean syntax for working with protobufs in Kotlin without needing to modify protoc plugins to generate Kotlin-specific code around them, if it seems worth the potential new edge cases.


"Shadow" classes - typealias+extensions with teeth?
#2

This is not really something to do at language level. For any individual case there is a “simple” solution:

  val Bar.safeFoo get() = if(hasFoo()) getFoo() else null

You could use kapt to generate these automatically (they don’t need to live in the same file, they are extension properties), but you’d need to be careful with your filtering.


#3

Yeah, that’s what I figured had to be done to use it on the same objects. What I was hoping was for a way to be able to use the properties cleanly and not need a prefix like ‘safe’, but it isn’t the end of the world if that is the only reasonable way.