I'm new at Kotlin and I found that in Kotlin you can create a method where a parameter has a default value.
For example
fun something( name: String, gender: String? = null): String{
return if(gender != null) {
doSomething
} else {
doSomethingElse
}
That makes me wonder, what's the best option in cases that the parameter could be null or empty?. Is this a bad practice?
Personally, I consider the correct way of solving cases like these it's to apply oop overloading. Any clue?
Related
this custom function call's a lambda block when null.
I expected the function definition below to enforce the same return type. : T
Is their a way to enforce that be block returns type T ?
inline fun <T> T?.whenNull(block: () -> T): T {
if (this == null) {
return block() //accepts any return type
} else {
return this
}
}
fun main() {
val x : Int? = null
println(x ?: 42)
println(x.whenNull { 42 })
println(x.whenNull { "why is kotlin not enforcing return of the same type?" })
}
T in the second whenAll call is being inferred as Any. Imagine that all occurrences of T are replaced Any, the call would be valid, wouldn't you agree? Int and String are both subtypes of Any, after all.
inline fun Any?.whenNull(block: () -> Any): Any {
if (this == null) {
return block()
} else {
return this
}
}
fun main() {
println(x.whenNull { "why is kotlin not enforcing return of the same type?" })
}
Basically, the Kotlin compiler is "trying too hard" here to make your code compile, and infers an unexpected type for your type parameter.
There exists an internal annotation #kotlin.internal.InputTypesOnly that would prevent your code from compiling if the type inferred is not mentioned in one of the input types (parameter types, receiver type, etc) of the function.
In this case, the input type is just Int?, and T is inferred to be Any, so it would be make your code not compile as expected. Unfortunately though, this annotation is internal, and you cannot use it :( KT-13198 is the ticket about making it public.
Interestingly, when passing this to the block the type is preserved and works as expected.
inline fun <T> T?.whenNullAlt(block: (T?) -> T): T {
if (this == null) {
return block(this) // "this" is superfluous, but enforces same return type
} else {
return this
}
}
fun main() {
val x : Int? = null
println(x.whenNullAlt { 42 })
println(x.whenNullAlt { "does not compile" })
}
As other answers pointed out, this is technically correct from Kotlin viewpoint because it can infer T to Any and it would compile.
However, while technically correct, this is a known problem in Kotlin language and is going to be fixed some day. That noted, let's try to understand why your answers's code works while your questions' doesn't.
The reason for this is that lambdas are always inferred last: imagine it as being a queue on what parts of the expression need to have their types inferred and anything inside a lambda is always at the end of the queue, no matter where the lambda is in the expression. So, when going over your example, it infers everything else, decides that the type of this should be Int?, than goes to the lambda, sees a String return type and merges them into Any being very proud of itself.
In the other example, however, the lambda is passed an external fact about it's parameter — the already inferred Int from the receiver (lambda is always the last to get the info, remember?). That way the inference inside the lambda fails because the argument and the result type are in disagreement.
I was surprised that this code compiled in Kotlin.
fun foo(key: String, value: Int?) {
if (value == null) {
bar(value)
}
}
fun bar(key: String?) {
}
As you can see foo passes value of type Int? to bar as String?. I guess this compiles because value must be null in this context, but apparently, bar(value) in foo was a typo of bar(key).
Are there any compiler options to make this an error or a warning, or are there common practices to prevent this error? I'd also like to know in which use cases this behavior is useful.
I'm using Kotlin version 1.3.50-release-112 (JRE 1.8.0_152-b16).
Note that this code doesn't compile (as I expected).
fun foo(key: String, value: Int?) {
bar(value)
}
fun bar(key: String?) {
}
with this error.
k2.kt:2:9: error: type mismatch: inferred type is Int? but String? was expected
bar(value)
are there common practices to prevent this error?
Yes - avoid using null and/or accepting nullable parameters.
The method fun bar(key: String?) is actually two different functions - one with and one without input. The implementation could look like:
fun foo(key: String, value: Int?) {
if (value == null) {
barEmpty()
} else {
/*Something else*/
}
}
fun bar(key: String) {}
fun barEmpty() {}
Simply, I want a function like:
fun <T> convert(val foo: String, fooT: KType) : T {
...?
}
For Int, it would return foo.toInt(), for Double, foo.toDouble(), and to some unknown type, just throw an exception. I think it's not so hard to create my own switch statement for the types I expect, but out of curiosity - is there a way already?
Recommended way
Unfortunately, there's no easy generic way because we're not dealing with casts, but method calls. This would be my approach:
fun <T> convert(str: String, type: KType) : T {
val result: Any = when (type.jvmErasure)
{
Long::class -> str.toLong()
Int::class -> str.toInt()
Short::class -> str.toShort()
Byte::class -> str.toByte()
...
else -> throw IllegalArgumentException("'$str' cannot be converted to $type")
}
return result as T // unchecked cast, but we know better than compiler
}
Usage:
#UseExperimental(ExperimentalStdlibApi::class)
fun main() {
val int = convert<Int>("32", typeOf<Int>())
println("converted: $int")
}
Instead of a KType parameter, you could also use a Class<T> and make the function reified, so it can be called as convert<Int>("32") or even "32".toGeneric<Int>().
Hardcore way
While there is no easy way, it is possible to access the type using heavy reflection and relying on implementation details. For this, we can extract the type name from the KType object, find an extension method (in a different class) that matches, and call it using reflection.
We have to use to*OrNull() instead of to*(), because the latter is inline and won't be found by reflection. Also, we need to resort to Java reflection -- at this time, Kotlin reflection throws UnsupportedOperationException for the types involved.
I do not recommend this in productive code, as it's inefficient and can break with future standard library versions, but it's a nice experiment:
fun convert(str: String, type: KType): Any {
val conversionClass = Class.forName("kotlin.text.StringsKt")
// here, the to*OrNull() methods are stored
// we effectively look for static method StringsKt.to*OrNull(String)
val typeName = type.jvmErasure.simpleName
val funcName = "to${typeName}OrNull" // those are not inline
val func = try {
conversionClass.getMethod(funcName, String::class.java) // Java lookup
} catch (e: NoSuchMethodException) {
throw IllegalArgumentException("Type $type is not a valid string conversion target")
}
func.isAccessible = true // make sure we can call it
return func.invoke(null, str) // call it (null -> static method)
?: throw IllegalArgumentException("'$str' cannot be parsed to type $type")
}
Basically I have a function that does some stuff but specifically it checks if two values I'm passing are null. This function is called from various places to make sure those two values are not null. Kotlin is complaining later that I'm not checking for null. Is there a way I can do this so that Kotlin already knows that I am checking for null without using !!?
Here is a simple example:
private fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
private fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
private fun testNull(possibleNull: String?): Boolean {
return possibleNull != null
}
Basically testNull is only true if possibleNull is not null and that check is on an if right before calling mustPassNonNull so can I let Kotlin know I'm already checking that? without using !! of course.
Thanks.
It is possible with the use of contracts. Currently in experimental in Kotlin 1.3.
It is possible to declare contracts for your own functions, but this feature is experimental, as the current syntax is in a state of early prototype and will most probably be changed. Also, please note, that currently the Kotlin compiler does not verify contracts, so it's a programmer's responsibility to write correct and sound contracts. -kotlinlang.org
#ExperimentalContracts
fun stuff() {
var possibleNull: String? = "test"
if (testNull(possibleNull)) {
mustPassNonNull(possibleNull)
}
}
fun mustPassNonNull(possibleNull: String) {
//use value that isn't null
}
#ExperimentalContracts
fun testNull(possibleNull: String?): Boolean {
contract{
returns(true) implies (possibleNull is String)
}
return possibleNull != null
}
Articles I referenced:
https://kotlinlang.org/docs/reference/whatsnew13.html
https://blog.kotlin-academy.com/understanding-kotlin-contracts-f255ded41ef2
It seems like a simple let situation
private fun stuff() {
var possibleNull: String? = "test"
possibleNull?.let { mustPassNonNull(it) }
}
This way mustPassNonNull will know that it isn't null :)
Also, if you need to do more than just check for nullability you could do:
possibleNull
?.takeIf { /* it's not null here anymore, add any checks you need */}
?.let { /* both non-null and checked for whatever you need */}
What's the best way to get an instance of a generic type in Kotlin? I am hoping to find the best approximation of the following C# code:
public T GetValue<T>() where T : new() {
return new T();
}
EDIT: As mentioned in comments, this is probably a bad idea. Accepting a () -> T is probably the most reasonable way of achieving this. That said, the following technique will achieve what you're looking for, if not necessarily in the most idiomatic way.
Unfortunately, you can't achieve that directly: Kotlin is hamstrung by its Java ancestry, so generics are erased at run time, meaning T is no longer available to use directly. Using reflection and inline functions, you can work around this, though:
/* We have no way to guarantee that an empty constructor exists, so must return T? instead of T */
inline fun <reified T : Any> getValue(): T? {
val primaryConstructor = T::class.constructors.find { it.parameters.isEmpty() }
return primaryConstructor?.call()
}
If we add some sample classes, you can see that this will return an instance when an empty constructor exists, or null otherwise:
class Foo() {}
class Bar(val label: String) { constructor() : this("bar")}
class Baz(val label: String)
fun main(args: Array<String>) {
System.out.println("Foo: ${getValue<Foo>()}") // Foo#...
// No need to specify the type when it can be inferred
val foo : Foo? = getValue()
System.out.println("Foo: ${foo}") // Foo#...
System.out.println("Bar: ${getValue<Bar>()}") // Prints Bar#...
System.out.println("Baz: ${getValue<Baz>()}") // null
}