I'm getting Overload resolution ambiguity error on kotlin safe call - kotlin

I have a nullable string variable ab. If I call toUpperCase via safe call operator after I assign null to it, kotlin gives error.
fun main(args: Array<String>){
var ab:String? = "hello"
ab = null
println(ab?.toUpperCase())
}
Error:(6, 16)
Overload resolution ambiguity:
#InlineOnly public
inline fun Char.toUpperCase(): Char defined in kotlin.text
#InlineOnly public inline fun String.toUpperCase(): String defined in kotlin.text
What's the problem here?

As stated in this doc about smart-casts:
x = y makes x of the type of y after the assignment
The line ab = null probably smart casts ab to Nothing?. If you check ab is Nothing? it is indeed true.
var ab: String? = "hello"
ab = null
println(ab?.toUpperCase())
println(ab is Nothing?) // true
Since Nothing? is subtype of all types (including Char? and String?), it explains why you get the Overload resolution ambiguity error. The solution for this error will be what Willi Mentzel mentioned in his answer, casting ab to the type of String before calling toUpperCase().
Remarks:
This kind of error will occur when a class implements two interfaces and both interface have extension function of the same signature:
//interface
interface A {}
interface B {}
//extension function
fun A.x() = 0
fun B.x() = 0
//implementing class
class C : A, B {}
C().x() //Overload resolution ambiguity
(C() as A).x() //OK. Call A.x()
(C() as B).x() //OK. Call B.x()

I'm not sure but that seems to be a bug due to smart casting (to Nothing?, subtype of every nullable type). This one works:
fun main(args: Array<String>) {
var ab: String? = "hello"
ab = makeNull()
println(ab?.toUpperCase())
}
fun makeNull(): String? = null
The only difference: The compiler does not know the null assignment directly, which seems to cause the error in your example. But still, yours should probably work too.

It really seems like a bug. The type String? is lost somehow upon assigning null, so you have to tell the compiler explicitely that it should deal with a String?.
fun main(args: Array<String>){
var ab: String? = "hello"
ab = null
println((ab as String?)?.toUpperCase()) // explicit cast
// ...or
println(ab?.let { it.toUpperCase() }) // use let
}

I believe this is due to smart casts used by Kotlin. In other words, Kotlin is able to infer that after this line of code:
ab = null
type of variable ab is simply null (this is not actual type you can use in Kotlin - I am simply referring to range of allowed values), not String? (in other words, there is no way ab might contain a String).
Considering that toUpperString() extension function is defined only for Char and String (and not Char? or String?), there is no way to choose between them.
To avoid this behaviour see answers proposed by other guys (e.g. explicit casting to String?), but this definitely looks like a feature (and quite a useful one) rather than a bug for me.

I decompiled your function and I figured: after the moment you make ab = null the compiler will smartcast it, putting null (ACONST_NULL) in every ocurrence of ab. Then as null has no type. you can't infer the type for the receiver of toUpperCase().
This is the java equivalent code generated from the kotlin byte code:
public final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
String ab = "hello";
ab = (String)null;
Object var3 = null;
System.out.println(var3);
}
It looks as an issue that should be resolved by the kotlin team.

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.

Extension method, when called on a null object, is called on the wrong type

fun main() {
val set: Set<Int>?
set = null
val emptySet: Set<Int> = set.orEmpty()
}
Can't figure out why even when explicitly typing the set variable as Set <Int>? the compiler considers that in the extension method set.orEmpty () set - is a string and, accordingly, crashes with an error:
Kotlin: Type mismatch: inferred type is String but Set was expected
But when declaring and initializing in one line, everything happens correctly:
fun main() {
val set: Set<Int>? = null
val emptySet: Set<Int> = set.orEmpty()
}
The behavior you're observing can be explained by the interaction of two Kotlin features:
first, the type of set variable is narrowed to Nothing? as a result of a smart cast after the assignment of null value to it. The smart cast after an assignment can be useful in cases when it narrows variable type to a more specific type, but narrowing to Nothing? does more harm than good.
second, among all overloads of orEmpty function available for a value of type Nothing?, the non-generic one String?.orEmpty() is chosen due to the specific rule of Kotlin overload resolution: a non-generic candidate is preferred to generic ones.
This behavior indeed can be puzzling, so I've reported this problem as KT-50661.
I think this is related to the fact that the compiler is not so smart that it could deduce that the code set = null will be executed exactly once – it could be zero times or more than once.
If you know that it will run exactly one, you can tell the compiler by using a feature called kotlin.contracts:
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
#ExperimentalContracts
fun main() {
val set: Set<Int>?
once { set = null }
val emptySet: Set<Int> = set.orEmpty()
}
#ExperimentalContracts
fun once(lambda: () -> Unit) {
contract { callsInPlace(lambda, InvocationKind.EXACTLY_ONCE) }
lambda()
}
See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.contracts/

Why do I get this? Kotlin: Type mismatch: inferred type is String? but String was expected

When I try to run this code:
fun main() {
val input: String = readLine()
val outputs = input.toCharArray()
for (i in 0 until input.length) {
print("${outputs[i]}${outputs[i]}")
}
}
I get this Error:(2, 25) Kotlin: Type mismatch: inferred type is String? but String was expected.
How do I fix that?
The readLine() function returns a String? (nullable String).
Return the line read or null if the input stream is redirected to a file and the end of file has been reached.
There is no end of File in the console inputs so there's no need to worry about nulls. You can use the unsafe call operator !! to cast it to a non null value String.
val input: String = readLine()!!
In Kotlin, calling a function on a nullable type using only the dot operator is a compilation error as it could cause a null pointer exception. In order to avoid this kind of error, the compiler forces you to check if your reference is not null. It is also possible to use the safe call operator ?.. I recommend you to read the excellent Kotlin documentation about the subject: Null Safety.
If you have started using Kotlin, I also recommend you to start writing your code in a more declarative/functional way. Look how it is possible to accomplish what you want in a much simpler way:
fun main() {
val input = readLine()
input?.toCharArray()?.forEach { print(it) }
}

How does erasure work in Kotlin?

In Kotlin, the following code compiles:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): Int {
return 2;
}
}
This code, however, does not:
class Foo {
fun bar(foo: List<String>): String {
return ""
}
fun bar(foo: List<Int>): String {
return "2";
}
}
Compiling this will cause the following error:
Error:(8, 5) Kotlin: Platform declaration clash: The following declarations have the same JVM signature (foo(Ljava/util/List;)Ljava/lang/String;):
fun foo(layout: List<Int>): String
fun foo(layout: List<String>): String
In Java, neither example will compile:
class Foo {
String bar(List<Integer> foo) {
return "";
}
Integer bar(List<String> foo) {
return 2;
}
}
class Foo {
String bar(List<Integer> foo) {
return "";
}
String bar(List<String> foo) {
return "2";
}
}
Unsurprisingly, both of the prior snippets generate the familiar compiler error:
Error:(13, 12) java: name clash: bar(java.util.List<java.lang.String>) and bar(java.util.List<java.lang.Integer>) have the same erasure
What surprises me is that the first Kotlin example works at all, and second, if it works, why does the second Kotlin example fail? Does Kotlin consider a method's return type as part of its signature? Furthermore, why do method signatures in Kotlin respect the full parameter type, in contrast with Java?
Actually Kotlin knows the difference between the two methods in your example, but jvm will not. That's why it's a "platform" clash.
You can make your second example compile by using the #JvmName annotation:
class Foo {
#JvmName("barString") fun bar(foo: List<String>): String {
return ""
}
#JvmName("barInt") fun bar(foo: List<Int>): String {
return "2";
}
}
This annotation exists for this very reason. You can read more in the interop documentation.
While #Streloks answer is correct, I wanted to dig deeper regarding why it works.
The reason why the first variant works, is that it is not prohibited within the Java Byte code. While the Java compiler complains about it, i.e. the Java language specification does not allow it, the Byte code does, as was also documented in https://community.oracle.com/docs/DOC-983207 and in https://www.infoq.com/articles/Java-Bytecode-Bending-the-Rules. In the Byte code every method call refers the actual return type of the method, which isn't that way when you write the code.
Unfortunately I couldn't find the actual source, why it is that way.
The document regarding Kotlins name resolution contains some interesting points, but I did not see your actual case there.
What really helped me understand it, was the answer from #Yole to Kotlin type erasure - why are functions differing only in generic type compilable while those only differing in return type are not?, more precisely that the kotlin compiler will not take the type of the variable into account when deciding which method to call.
So, it was a deliberate design decision that specifying the type on a variable will not influence which method is the one to be called but rather the other way around, i.e. the called method (with or without generic information) influences the type to be used.
Applying the rule on the following samples then makes sense:
fun bar(foo: List<String>) = "" (1)
fun bar(foo: List<Int>) = 2 (2)
val x = bar(listOf("")) --> uses (1), type of x becomes String
val y = bar(listOf(2)) --> uses (2), type of y becomes Int
Or having a method supplying a generic type but not even using it:
fun bar(foo: List<*>) = "" (3)
fun <T> bar(foo: List<*>) = 2 (4)
val x = bar(listOf(null)) --> uses (3) as no generic type was specified when calling the method, type of x becomes String
val y = bar<String>(listOf(null)) --> uses (4) as the generic type was specified, type of y becomes Int
And that's also the reason why the following will not work:
fun bar(foo: List<*>) = ""
fun bar(foo: List<*>) = 2
This is not compilable as it leads to a conflicting overload as the type of the assigned variable itself is not taken into consideration when trying to identify the method to be called:
val x : String = bar(listOf(null)) // ambiguous, type of x is not relevant
Now regarding that name clash: as soon as you use the same name, the same return type and the same parameters (whose generic types are erased), you will actually get the very same method signature in the byte code. That's why #JvmName becomes necessary. With that you actually ensure that there are no name clashes in the byte code.

How to pass nullable type into function that takes a non null type?

Is this possible if I do a null check before passing? For example:
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
if (num != null) doSomething(num)
}
fun doSomething(number: Int) {
...
}
I don't understand why the compiler won't allow me to pass a nullable even though I check that it's not null first. Can anyone explain?
NOTE: starting from compiler version 1.0 beta the code in question works as is
The compiler can tell if the variable is mutated between check and use, at least in case of local variables like in this question, and in some other cases. See Jayson's answer for details.
http://kotlinlang.org/docs/reference/null-safety.html#checking-for-null-keyword--in-conditions says
The compiler tracks the information about the [null] check ... this only works where b is immutable (i.e. a local val or a member val which has a backing field and is not overridable), because otherwise it might happen that b changes to null after the check.
So something like this should work:
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
val numVal: Int? = num
if (numVal != null) doSomething(numVal)
}
fun doSomething(number: Int) {
...
}
Of course, it would be nicer to rewrite "stuff happens" in such a way that you could make num into a val in the first place.
In current Kotlin (1.0 beta or newer) you do not have this issue anymore. Your code would compile. A local variable that is val or var can safely Smart Cast since the compiler can determine if the value could have mutated or not (on another thread for example).
Here is an excerpt from another Stack Overflow question that covers more aspects of nullability and Kotlin's operators for dealing with them.
More about null Checking and Smart Casts
If you protect access to a nullable type with a null check, the compiler will smart cast the value within the body of the statement to be non nullable. There are some complicated flows where this cannot happen, but for common cases works fine.
val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
// allowed to reference members:
possiblyXyz.foo()
// or also assign as non-nullable type:
val surelyXyz: Xyz = possibleXyz
}
Or if you do a is check for a non nullable type:
if (possibleXyz is Xyz) {
// allowed to reference members:
possiblyXyz.foo()
}
And the same for 'when' expressions that also safe cast:
when (possibleXyz) {
null -> doSomething()
else -> possibleXyz.foo()
}
// or
when (possibleXyz) {
is Xyz -> possibleXyz.foo()
is Alpha -> possibleXyz.dominate()
is Fish -> possibleXyz.swim()
}
Some things do not allow the null check to smart cast for the later use of the variable. The example above uses a local variable that in no way could have mutated in the flow of the application, whether val or var this variable had no opportunity to mutate into a null. But, in other cases where the compiler cannot guarantee the flow analysis, this would be an error:
var nullableInt: Int? = ...
public fun foo() {
if (nullableInt != null) {
// Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
val nonNullableInt: Int = nullableInt
}
}
The lifecycle of the variable nullableInt is not completely visible and may be assigned from other threads, the null check cannot be smart cast into a non nullable value. See the "Safe Calls" topic below for a workaround.
Another case that cannot be trusted by a smart cast to not mutate is a val property on an object that has a custom getter. In this case the compiler has no visibility into what mutates the value and therefore you will get an error message:
class MyThing {
val possibleXyz: Xyz?
get() { ... }
}
// now when referencing this class...
val thing = MyThing()
if (thing.possibleXyz != null) {
// error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
thing.possiblyXyz.foo()
}
read more: Checking for null in conditions
You can use let to simplify the code. The kotlin scope function introduces a local variable in the context of "num". No need to declare temporary variable numVal.
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
num?.let{
doSomething(it)
}
}
Which works same as below but simpler and cleaner.
fun main(args: Array<String>) {
var num: Int? = null
// Stuff happens that might make num not null
...
val numVal: Int? = num
if (numVal != null) doSomething(numVal)
}
Use can use Scoping function let or apply along with null safe operator ?.
fragmentManager?.let{
viewPager.adapter = TasksPagerAdapter(it)
}
This way you can pass a nullable type to a non-nullable type parameter