How to store boolean property for Parcelable interface? - kotlin

I have a data object, User. One of the properties of User is a Boolean. I can't figure out how to store this Boolean as there is no such writeBoolean() provided out of the box.
From what I've searched online, one way of storing the Boolean property is to use the writeInt() method and a ternary operator.
So I tried it here:
data class User(val contactNumber: String,
val email: String,
val fullName: String,
val isAdmin: Boolean,
val organization: String,
val unitNumber: String) : Parcelable {
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeString(contactNumber)
dest?.writeString(email)
dest?.writeString(fullName)
dest?.writeInt(isAdmin ? 1 : 0)
dest?.writeString(organization)
dest?.writeString(unitNumber)
}
}
But this syntax seems to only work with Java and not in Kotlin. I'm getting a compiler error indicating Unexpected tokens (use ':' to separate expressions on the same line. How do I accomplish this in Kotlin?

The ternary operator is not supported in kotlin
Use if-else instead
writeInt(if(isAdmin) 1 else 0)
Im using writeValue instead, it also usefull for nullable variables
dest.writeValue(this.booleanVar)
booleanVar = parcel.readValue(Boolean::class.java.classLoader) as? Boolean?
if it could be nullable and if not add ?: false
upd: as pointed in other answers dest variable cannot be null. afaik it marked as nullable after code converting using Android Studio. If you use that feature better to double check your code because of convertation could work not properly sometimes.
About ?. in general. You can rewrite it with let operator
dest?.let { it ->
it.write(....)
or even better
dest ?: retrun

The syntax for Java's isAdmin ? 1 : 0 in Kotlin is if (isAdmin) 1 else 0.
dest can't reasonably be null, so you can change its type to Parcel and all the ?. to ..
But the real answer is: use #Parcelize and let the compiler generate all that code, so you can't forget to change it when you add new properties, or get the order wrong, or...

1.) use this handy extension function:
fun Parcel.readBoolean(): Boolean = readByte() > 0
fun Parcel.writeBoolean(bool: Boolean) {
writeByte((if (bool) 1 else 0).toByte())
}
2.) in
override fun writeToParcel(dest: Parcel?, flags: Int) {
dest: Parcel! is a platform type, so if you know that it will never be null, then there is zero reason to specify it as a nullable type.
Use override fun writeToParcel(dest: Parcel, flags: Int) { instead.

Related

Different results on similar code with safe call operator in Kotlin

I'm new to Kotlin and these two below codes give different results.
fun main() {
var name: String? = "Rajat"
name = null
print(name?.toLowerCase())
}
Output: Compilation Error (illegal access operation)
fun main() {
var name: String? = null
print(name?.toLowerCase())
}
Output: null
When you do this assignment:
name = null
name is smart casted to Nothing?, which is problematic. Nothing is the subtype of every type, and so you become able to call any accessible extension functions of any type, according to the overload resolution rules here.
Compare:
fun main() {
var name: String? = "Denis"
name = null
print(name?.myExtension()) // works
val nothing: Nothing? = null
print(nothing?.myExtension()) // also works
}
fun Int.myExtension(): Nothing = TODO()
Note that allowing you to call any extension function on Nothing is perfectly safe - name is null anyway, so nothing is actually called.
Char.toLowerCase and String.toLowerCase happen to be two of the extension functions that are accessible, and you can call both on name, which is now a Nothing?. Therefore, the call is ambiguous.
Note that smart casts only happens in assignments, not in initialisers like var name: String? = null. Therefore, name is not smart casted to Nothing? in this case:
fun main() {
var name: String? = null
print(name?.toLowerCase()) // better to use lowercase(), toLowerCase is deprecated!
}
For the reason why, see my answer here.
The actual error on your first example is
Overload resolution ambiguity: public inline fun Char.toLowerCase(): Char defined in kotlin.text public inline fun String.toLowerCase(): String defined in kotlin.text
Looks like the Kotlin compiler is being too smart for its own good here. What's happening, is that on the second example, you are explicitly defining a variable of type String? and assigning it some value (null in this case, but that doesn't matter).
On the second example, you are defining a variable of some type, and then telling the compiler "hey, after this assignment, name is always null". So then it remembers the more-specific "name is null" instead of "name is String?".
The standard library has two methods called toLowerCase, one on Char and one on String. Both of them are valid matches now, and the compiler is telling you it doesn't know which one to pick. In the end that won't matter, because name is null, but the compiler apparently doesn't use that final thing to throw out the method call altogether.

Best Kotlin builder implementation ever?

I work with a lot's of generated kotlin data classes (openapi-generated) with val's and two common fields available and processed. So i can assign values only on construction, like:
data class StringRepresentation {
val value: String,
val available: Boolean,
val processed: Boolean,
}
data class DoubleRepresentation {
val value: Double,
val available: Boolean,
val processed: Boolean,
}
And i have to init lot's of them with common robust code, like:
val currentRepresentation = StringRepresentation("Something", true, false)
Can any pattern or Kotlin language support be used to remove robost object initialization?
It could be wonderful to use some kind of generic template method, something like this:
private inline fun <reified T: StringRepresentation> buildRepresentation(
value: Any,
available: Boolean,
processed: Boolean
): T {
return when(T) {
is StringRepresentation -> StringRepresentation(value.toString(), available, processed)
else -> ...
}
}
, but my types and properties are final and also syntax doesn't allow to set multiple generic boundaries. I can't figure it out the right approach for this. I guess in that case I need to write a builder, but this seems to be java way.
Is there any kotlin way to do this?
I do not think that it is possible with your setup to write a builder that would actually be useful. Your *Representation data types stand in no explicit type relation to their respective type parameter (e.g. a StringRepresentation is not Something<String>), so what should be the return type of a generic builder function? It could only be Any and you would need to cast every result to its expected type in order to use it.
What you can do is to define a generic data class:
data class Representation<T>(
val value: T,
val available: Boolean,
val processed: Boolean,
)
I know, you cannot use that class as super class of your specific data classes, but you can write extension functions that convert the generic representation for one value type into its corresponding specific representation:
fun Representation<String>.typed() = StringRepresentation(value, available, processed)
fun Representation<Double>.typed() = DoubleRepresentation(value, available, processed)
Then you can use the same code to create a data object of any type:
val stringRepresentation: StringRepresentation = Representation("x", false, true).typed()
val doubleRepresentation: DoubleRepresentation = Representation(1.0, false, true).typed()
But please note that this is still not a generic solution since whatever you put into the constructor Representation has to be typed explicitly as a String or Double, respectively.
Let's say you define a generic function for all undefined value types:
fun <T: Any> Representation<T>.typed(): Any = error("no typed representation for value type ${value::class}")
The specific cases above will still work, and you could additionally write something like this:
val x : Any = 2.0
val someRep: Any = Representation(x, true, false).typed()
This is syntactically correct and will compile, but it will not work as desired, because what you get is an IllegalArgumentException ("no typed representation for value type class kotlin.Double"), because x is not typed as Double, but as Any.

How can I use annotation #IntRange for Kotlin extension function for Integer

below is my Kotlin extension function :
infix fun Int.send(data: String) {
MsgSendUtils.sendStringMsg(
this,
data
)
}
called like this :
8401 send "hi"
and my requirement is :
the caller must greater than 8400 .
How can i achieve that ?
Thanks !
In theory, you can use the syntax #receiver:IntRange(from = 8400) to apply an annotation to the receiver of a function:
infix fun #receiver:IntRange(from = 8400) Int.send(data: String) {
MsgSendUtils.sendStringMsg(this, data)
}
However, this doesn't seem to trigger the Android Lint error as you would expect. This is probably a missing feature in the linter itself when inspecting Kotlin code.
A workaround would be to declare the function differently (using this value as parameter instead of receiver):
fun send(#IntRange(from = 8400) id: Int, data: String) {
MsgSendUtils.sendStringMsg(id, data)
}
// or
fun String.sendTo(#IntRange(from = 8400) id: Int) {
MsgSendUtils.sendStringMsg(id, this)
}
Otherwise the best you could do in pure Kotlin would be to check the value at runtime:
infix fun Int.send(data: String) {
require(this >= 8400) { "This value must be greater than 8400" }
MsgSendUtils.sendStringMsg(this, data)
}
Note that, depending on what this value represents, I would advise using a more specific type instead of Int. For example, if your Int represents an ID, maybe you should instead declare a value class wrapping this integer. You can then enforce the constraints at construction time (still at runtime, but it's less error-prone).
This approach would also avoid polluting auto-completion on all integers, which can be pretty annoying on a big project given how specific this function is.

is there any way I send a nullable Function<T,R> as parameter in Kotlin?

I am trying to use the public interface Function (as I learned it in Java) in Kotlin.
For this I created my method
fun foo(input: List<String>, modifier1: Function<List<String>>? = null){
}
as far I remember here I should be able to do modifier1.apply(input)
but seems like it is not possible (it is possible to do modifier1.apply{input} though)
Reading more about it I found this:
Kotlin: how to pass a function as parameter to another?
So I changed my method signature to this:
fun foo(input:String, modifier2: (List<String>) -> (List<String>){
}
Here I am able to do modifier2(input)
and I can call foo this way
service.foo(input, ::myModifierFunction)
where
fun myModifierFunction(input:List<String>):List<String>{
//do something
return input
}
So far this seems possible but it is not acceptable to have the function reference as nullable, is there any way I can do that? or use Function ?
You were using kotlin.Function instead of java.util.function.Function in your first example. Note that the latter takes 2 generic types: 1 for the incoming parameter and 1 for the resulting one.
The apply method you saw is the default Kotlin one: apply, not the one of Java's Function-interface.
If you really want to have the Java-function as nullable type the following should work:
fun foo(input: List<String>, modifier1: java.util.function.Function<List<String>, List<String>>? = null) {
modifier1?.apply(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Kotlin variant for the same:
fun foo(input: List<String>, modifier1: ((List<String>) -> List<String>)? = null) {
modifier1?.invoke(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Maybe also a default function, such as { it } instead of null might better suite your needs? (Java variant would be Function.identity()):
// java modifier1 : Function<List<String>, List<String>> = Function.identity()
// kotlin modifier1 : (List<String>) -> List<String> = { it }
You can make the reference nullable simply with ? — the only wrinkle is that the whole function type needs to be in parens first:
fun foo(input: String, modifier2: ((List<String>) -> List<String>)? = null) {
}
As required, modifier2 is optional; if specified, it may contain null, or it may contain a function taking and returning a list of strings.
As mentioned in another answer, kotlin.Function is not the same as java.util.function.Function — though in practice you shouldn't need to refer to either directly, as the -> notation is simpler.
If you want to pass in a function that takes List<String> as its parameter and returns nothing meaningful, the type for you is Function1<List<String>, Unit>. The method name for invoking a function is invoke(), which you could also do with just regular parentheses, if it wasn't nullable. All in all, your code could look something like this:
fun foo(input: List<String>, modifier1: Function1<List<String>, Unit>? = null) {
modifier1?.invoke(input)
}
The 1 in the typename of Function1 means that it's a one parameter function, there's also Function0, Function2, etc.
The Function type on its own is not something you can use to call that function, as it's an empty marker interface. All functions implement this regardless of how many parameters they have.

Any way to tell Kotlin compiler that prior callable function already did a check for null

If I have a code fragment like
val myNullableString : String?
val resultingString = if (myNullableString.isNullOrBlank())
myNullDefaultString else String.format(myNullableString!!, someOtherString)
I have to use myNullableString!! in String.format() since the compiler would not be able to figure out that isNullOrBlank() includes a null-check. Is this correct or is there any way to tell the compiler that a function will infer that the instance is not null?
Kotlin 1.3 introduced a concept called contracts. From now on you can tell the compiler that a null check has been done. It has already been implemented for all necessary functions in the standard library (for isNullOrBlank() too).
So, your example would compile now without !!
val myNullableString: String? = null
val result = if (myNullableString.isNullOrBlank()) {
"default"
} else {
String.format(myNullableString, "hello") // myNullableString is smart-casted to String
}
Didn’t see any mentions that there is a way to tell such info to the compiler, but here is a workaround: having function returning null if string is empty and use let?.{} after that
inline fun String?.nullIfBlank(): String? = if (this?.isBlank() ?: true) null else this
val resultingString: String = myNullableString.nullIfBlank()?.let {
String.format(it)
} ?: myNullDefaultString
Compiler can perform smart cast from nullable type to non-nullable only after explicit check expression, like myNullableString != null