Kotlin contract which helps compiler smartcast all list elements from nullable to non-nullable? - kotlin

following situation: I try to implement a generic function, which checks if a list of variables are all not null and executes a lambda, which requires non-nullable variables.
I can chain multiple let-calls or implement multiple 'safeLet'-Function, with 2,3,4... arguments, but I still hope one generic function with a list is possible.
Here the current code, with chained let-calls:
val parameters = call.receiveParameters()
val firstName = parameters["firstName"]
val lastName = parameters["lastName"]
firstName?.let {
lastName?.let { userService.add(UserDTO(firstName = firstName, lastName = lastName)) }
}
Here is my current 'safeLet' function:
fun <T> List<Any?>.safeLet(block: () -> T) {
if(this.contains(null)) return
block()
}
But following still doesn't compile (because parameters of UserDTO are String and not String?):
listOf(firstName, lastName).safeLet {
userService.add(UserDTO(firstName = firstName, lastName = lastName))
}
I can add !! after firstName and lastName to avoid the nullcheck, but that's ugly.
My idea is to use kotlin contracts. Is something possible like this:
#ExperimentalContracts
fun <T> List<Any?>.safeLet(block: () -> T) {
contract {
returnsNotNull() implies {ALL ELEMENTS ARE NOT NULLABLE}
}
if(this.contains(null)) return
block()
}
Thanks in advance.
In relation to the "filterNotNull" comment i now tried this. Still not ideal, because I don't like to use this[0] and this[1] here, but it works:
allNotNull(firstName, lastName)?.apply {
userService.add(UserDTO(firstName = this[0], lastName = this[1]))
}
fun <T : Any> allNotNull(vararg elements: T?): List<T>? = if(elements.contains(null)) null else elements.filterNotNull()

You can use a binding function. It accepts another function inside of which you can use bind to transform nullable reference to not-null one.
If you pass a not-null argument to bind, it returns it. Otherwise, it suspends the execution of the binding block.
If the execution is suspended, binding returns null, otherwise it returns a result of the binding block.
Here is how you can use binding:
binding { userService.add(UserDTO(firstName = firstName.bind(), lastName = lastName.bind())) }
One more example:
fun sumOrNull(a: Int?, b: Int?): Int? = binding { a.bind() + b.bind() }
Here is my binding implementation:
// startCoroutineUninterceptedOrReturn returns either COROUTINE_SUSPENDED or R
#Suppress("UNCHECKED_CAST")
fun <R> binding(block: suspend Binder.() -> R): R? =
when (val result = block.startCoroutineUninterceptedOrReturn(Binder, BinderContinuation)) {
COROUTINE_SUSPENDED -> null
else -> result as R
}
#RestrictsSuspension
object Binder {
suspend fun <T> T?.bind(): T {
if (this != null) return this
suspendCoroutine<Nothing> {}
}
}
suspend fun <T> Binder.bind(obj: T?): T {
contract {
returns() implies (obj != null)
}
return obj.bind()
}
private object BinderContinuation : Continuation<Any?> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
override fun resumeWith(result: Result<Any?>) {
result.getOrThrow()
}
}

Related

Kotlin - TypeReference<T> Cannot obtain Class<*> For Type Arguments

I've created a Kotlin equivalent of TypeReference<T> like so:
abstract class TypeReference<T> : Comparable<T> {
val type: Type get() = getGenericType()
val arguments: List<Type> get() = getTypeArguments()
final override fun compareTo(other: T): Int {
return 0
}
private fun getGenericType(): Type {
val superClass = javaClass.genericSuperclass
check(superClass !is Class<*>) {
"TypeReference constructed without actual type information."
}
return (superClass as ParameterizedType).actualTypeArguments[0]
}
private fun getTypeArguments(): List<Type> {
val type = getGenericType()
return if (type is ParameterizedType) {
type.actualTypeArguments.toList()
} else emptyList()
}
}
In order to obtain Class<*> of the generic type and its arguments, I've also created the following extension function (and this is where I believe the problem lies, since this is where the stack trace fails).
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
else -> Class.forName(typeName)
}
I'm unit testing this like so:
#Test
fun `TypeReference should correctly identify the List of BigDecimal type`() {
// Arrange
val expected = List::class.java
val expectedParameter1 = BigDecimal::class.java
val typeReference = object : TypeReference<List<BigDecimal>>() {}
// Act
val actual = typeReference.type.toClass()
val actualParameter1 = typeReference.arguments[0].toClass()
// Assert
assertEquals(expected, actual)
assertEquals(expectedParameter1, actualParameter1)
}
The problem I think, lies in the extension function else -> Class.forName(typeName) as it throws:
java.lang.ClassNotFoundException: ? extends java.math.BigDecimal
Is there a better way to obtain the Class<*> of a Type, even when they're generic type parameters?
You need to add is WildcardType -> ... branch to your when-expression to handle types like ? extends java.math.BigDecimal (Kotlin equivalent is out java.math.BigDecimal), ?(Kotlin equivalent is *), ? super Integer(Kotlin equivalent is in java.math.Integer):
fun Type.toClass(): Class<*> = when (this) {
is ParameterizedType -> rawType.toClass()
is Class<*> -> this
is WildcardType -> upperBounds.singleOrNull()?.toClass() ?: Any::class.java
else -> Class.forName(typeName)
}
Note that in this implementation single upper bound types will be resolved as its upper bound, but all other wildcard types (including multiple upper bounds types) will be resolved as Class<Object>
https://github.com/pluses/ktypes
val typeReference = object : TypeReference<List<BigDecimal>>() {}
val superType = typeReference::class.createType().findSuperType(TypeReference::class)!!
println(superType.arguments.first())// List<java.math.BigDecimal>
println(superType.arguments.first().type?.arguments?.first())// java.math.BigDecimal

Is there a way to distinguish between a function argument's default value having been passed explicitly or implicitly in Kotlin?

Assuming a kotlin function like this:
fun f(p1: T1? = null, p2: T2? = null, ..., pN: TN? = null) {
// ...
}
Can the above function's implementation distinguish between the following two calls, where the first one passed p1 = null implicitly, and the second one passed it explicitly?
f() // Implicit
f(null) // Explicit
f(p1 = null) // Explicit
Note: There could be arbitrary numbers of parameters
No, it cannot distinguish between those cases.
You could distinguish between them if you added a distinct overload, however.
Although I'd rather not use that approach in production, you could do something like I've done in the following snippet:
object Default {
val defaultMapping = mutableMapOf<KClass<*>, Any?>()
inline fun <reified T> get(): T? =
T::class.let {
defaultMapping[it] ?: it.java.constructors.getOrNull(0)?.let { c ->
try {
// NOTE: for now only parameterles constructor will work
c.newInstance()
} catch (e: Exception) {
e.printStackTrace()
null
}.also { v ->
defaultMapping[it] = v
}
} ?: run {
defaultMapping[it] = null
null
}
} as? T
inline fun <reified T> T.isDefault(): Boolean = defaultMapping[T::class] == this
}
inline fun <reified T> foo(bar: T? = Default.get()) {
if (bar?.isDefault() == true) println("bar: default is in use")
else println("bar: $bar")
}
fun main() {
foo<Any>()
foo(Default.get<Any>())
foo<Any>(null)
foo<Any>(bar = null)
foo(Any())
val a = Any()
foo(a)
foo(bar = a)
}
Note, that I have not polished the code in any way. Some parts are leftovers from several attempts (e.g. the part about the constructors.getOrNull(0)) and I don't intend to improve that.
Also: This simple approach only works with default constructors (see it.newInstance()) on the JVM. So that's no multi-platform solution in any way.
The result is something like
bar: default is in use
bar: default is in use
bar: null
bar: null
bar: java.lang.Object#41906a77
bar: java.lang.Object#4b9af9a9
bar: java.lang.Object#4b9af9a9
Again: Keep in mind, this is very simplistic, don't use that in production!

GSON-based DSL causing a NPE

I've been trying to create a Kotlin DSL for creating GSON JsonObjects with a JSON-like syntax. My builder looks like this
import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
init {
builder()
}
val result = JsonObject()
infix fun String.to(property: Number) = result.addProperty(this, property)
infix fun String.to(property: Char) = result.addProperty(this, property)
infix fun String.to(property: Boolean) = result.addProperty(this, property)
infix fun String.to(property: String) = result.addProperty(this, property)
infix fun String.to(property: JsonElement) = result.add(this, property)
infix fun String.to(properties: Collection<JsonElement>) {
val arr = JsonArray()
properties.forEach(arr::add)
result.add(this, arr)
}
operator fun String.invoke(builder: JsonObject.() -> Unit) {
val obj = JsonObject()
obj.builder()
result.add(this, obj)
}
}
fun json(builder: JsonBuilder.() -> Unit) = JsonBuilder(builder).result
And my test looks like this
fun main() {
val json = json {
"name" to "value"
"obj" {
"int" to 1
}
"true" to true
}
println(json)
}
However, upon execution it causes a NullPointerException pointing to the first String extension function used, which I don't find very descriptive as I don't see anything being nullable up to that point. Moreover, I don't see how it really differs from the regular execution which of course doesn't cause a NPE.
val json = JsonObject()
json.addProperty("name", "value")
val obj = JsonObject()
obj.addProperty("int", 1)
json.add("obj", obj)
json.addProperty("true", true)
My question is what's exactly causing the exception (and how to prevent it).
The issue is that you've specified the initialiser block earlier than the result object, causing it to be null when you come to use it - this can be visualised by the following (decompiled output of your code).
public JsonBuilder(#NotNull Function1 builder) {
Intrinsics.checkParameterIsNotNull(builder, "builder");
super();
builder.invoke(this);
this.result = new JsonObject();
}
Therefore, the solution is to move the declaration and initialisation of result earlier than the initialiser block.
class JsonBuilder(builder: JsonBuilder.() -> Unit) {
val result = JsonObject()
init {
builder()
}
// ...
}
And the result is now...
{"name":"value","int":1,"obj":{},"true":true}
EDIT: You'll also want to allow chaining with your DSL, and fix a bug you currently have.
operator fun String.invoke(builder: JsonBuilder.() -> Unit) {
val obj = JsonBuilder(builder).result
result.add(this, obj)
}
Which produces the correct result of
{"name":"value","obj":{"int":1},"true":true}

Kotlin equals and hash code generator

I am aware that in Kotlin classes will have an equals and hashcode created automatically as follows:
data class CSVColumn(private val index: Int, val value: String) {
}
My question is, is there a way to have the implementation just use one of these properties (such as index) without writing the code yourself. What was otherwise a very succinct class now looks like this:
data class CSVColumn(private val index: Int, val value: String) {
override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}
if (javaClass != other?.javaClass) {
return false
}
other as CSVColumn
if (index != other.index) {
return false
}
return true
}
override fun hashCode(): Int {
return index
}
}
In Java with Lombok, I can do something like:
#Value
#EqualsAndHasCode(of="index")
public class CsvColumn {
private final int index;
private final String value;
}
Would be cool if there were a way to tell Kotlin something similar.
From the Data Classes documentation you get:
Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body
So you have to implement equals() and hashCode() manually or with the help of a Kotlin Compiler Plugin.
You can't do something like this for data classes, they always generate equals and hashCode the same way, there's no way to provide them such hints or options.
However, they only include properties that are in the primary constructor, so you could do this for them to only include index:
data class CSVColumn(private val index: Int, value: String) {
val value: String = value
}
... except you can't have parameters in the primary constructor that aren't properties when you're using data classes.
So you'd have to somehow introduce a secondary constructor that takes two parameters, like this:
class CSVColumn private constructor(private val index: Int) {
var value: String = ""
constructor(index: Int, value: String) : this(index) {
this.value = value
}
}
... but now your value property has to be a var for the secondary constructor to be able to set its value.
All this to say that it's probably not worth trying to work around it. If you need an non-default implementation for equals and hashCode, data classes can't help you, and you'll need to implement and maintain them manually.
Edit: as #tynn pointed out, a private setter could be a solution so that your value isn't mutable from outside the class:
class CSVColumn private constructor(private val index: Int) {
var value: String = ""
private set
constructor(index: Int, value: String) : this(index) {
this.value = value
}
}
I wrote a little utility called "stem", which allows you to select which properties to consider for equality and hashing. The resulting code is as small as it can get with manual equals()/hashCode() implementation:
class CSVColumn(private val index: Int, val value: String) {
private val stem = Stem(this, { index })
override fun equals(other: Any?) = stem.eq(other)
override fun hashCode() = stem.hc()
}
You can see its implementation here.
I guess that we have to write equals()/hashCode() manually for now.
https://discuss.kotlinlang.org/t/automatically-generate-equals-hashcode-methods/3779
It is not supported and is planning to be, IMHO.
I guess that we have to write equals()/hashCode() manually for now. https://discuss.kotlinlang.org/t/automatically-generate-equals-hashcode-methods/3779
It is not supported and is planning to be, IMHO.
Below are some reference which may be helpful.
https://discuss.kotlinlang.org/t/how-does-kotlin-implement-equals-and-hashcode/940
https://kotlinlang.org/docs/reference/data-classes.html
https://medium.com/#appmattus/effective-kotlin-item-11-always-override-hashcode-when-you-override-equals-608a090aeaed
See the following performance optimized way (with the use of value classes and inlining) of implementing a generic equals/hashcode for any Kotlin class:
#file:Suppress("EXPERIMENTAL_FEATURE_WARNING")
package org.beatkit.common
import kotlin.jvm.JvmInline
#Suppress("NOTHING_TO_INLINE")
#JvmInline
value class HashCode(val value: Int = 0) {
inline fun combineHash(hash: Int): HashCode = HashCode(31 * value + hash)
inline fun combine(obj: Any?): HashCode = combineHash(obj.hashCode())
}
#Suppress("NOTHING_TO_INLINE")
#JvmInline
value class Equals(val value: Boolean = true) {
inline fun combineEquals(equalsImpl: () -> Boolean): Equals = if (!value) this else Equals(equalsImpl())
inline fun <A : Any> combine(lhs: A?, rhs: A?): Equals = combineEquals { lhs == rhs }
}
#Suppress("NOTHING_TO_INLINE")
object Objects {
inline fun hashCode(builder: HashCode.() -> HashCode): Int = builder(HashCode()).value
inline fun hashCode(vararg objects: Any?): Int = hashCode {
var hash = this
objects.forEach {
hash = hash.combine(it)
}
hash
}
inline fun hashCode(vararg hashes: Int): Int = hashCode {
var hash = this
hashes.forEach {
hash = hash.combineHash(it)
}
hash
}
inline fun <T : Any> equals(
lhs: T,
rhs: Any?,
allowSubclasses: Boolean = false,
builder: Equals.(T, T) -> Equals
): Boolean {
if (rhs == null) return false
if (lhs === rhs) return true
if (allowSubclasses) {
if (!lhs::class.isInstance(rhs)) return false
} else {
if (lhs::class != rhs::class) return false
}
#Suppress("unchecked_cast")
return builder(Equals(), lhs, rhs as T).value
}
}
This allows you to write a equals/hashcode implementation as follows:
data class Foo(val title: String, val bytes: ByteArray, val ignore: Long) {
override fun equals(other: Any?): Boolean {
return Objects.equals(this, other) { lhs, rhs ->
combine(lhs.title, rhs.title)
.combineEquals { lhs.bytes contentEquals rhs.bytes }
}
}
override fun hashCode(): Int {
return Objects.hashCode(title, bytes.contentHashCode())
}
}

Simpler or more functional way of chaining objects in Kotlin

I have created a helper method buildChain which essentially creates a
chain of objects given that they implement the interface IChain<T>
and set the contracts next member
The Code
interface Chain<T> {
var next: T?
operator fun plus(next: T): T?
}
fun <T : Chain<T>> buildChain(first: T, vararg members: T): T {
var next: T? = null
members.forEachIndexed { i, t ->
if (i == 0) {
next = first + t
} else {
next = next?.run { this + t }
}
}
return first
}
Implementation example
data class Person(val name: String) : Chain<Person> {
override var next: Person? = null
override fun plus(next: Person): Person? {
this.next = next
return next
}
}
fun createPersonChain()
= buildChain(Person("Bob"), Person("Bitzy"), Person("Blitzy"))
Implementaion output example
#JvmStatic fun main(args: Array<String>) {
var first = createPersonChain()
// first.name = "Bob"
// first.next.name = "Bitzy"
// first.next.next.name = "Blitzy"
}
Is there a functional or simpler way for acheiving the code above keeping the implementaion usage the same?
A functional idiom fold suits your needs well: it takes an initial item and then iterates over the other items, maintaining an accumulated value, which is updated on each item being processed with the function you provide.
In Kotlin, it is fold extension function for Iterable, Sequence or Array.
You can use it in the following way:
fun <T : Chain<T>> buildChain(first: T, vararg members: T): T {
members.fold(first as T?) { acc, i -> acc?.let { it + i } }
return first
}
Here first as T? cast is needed for the accumulator type to be inferred as nullable T?, because plus in your Chain<T> returns nullable value (by the way, is it necessary?).
You can also use foldRight, which just iterates in the opposite order:
fun <T : Chain<T>> buildChain(first: T, vararg members: T): T? =
(listOf(first) + members)
.foldRight(null as T?) { i, acc -> acc?.let { i + acc }; i }
And there are reduce and reduceRight with similar semantics but using the first and the last item respectively for the accumulator's initial value. Here's the example with reduceRight:
fun <T : Chain<T>> buildChain(first: T, vararg members: T): T? =
(listOf(first) + members).reduceRight { i, acc -> i.apply { plus(acc) } }
Try apply{}. In the {} block pass your methods separated with ';'
Object().apply{ method1(); signUp(user) }