Let's say I have a piece of code like:
fun temp2 (li : MutableList<Int>):Int {
if (li.isEmpty()) return 0
val pos=li.filter { it>0 }
val neg=li.filter { it<0 }
if (pos.isEmpty() && neg.isNotEmpty()){
// this gives compiling error because Required: Int, Found: Int?
// But I just checked one line higher that neg is Not Empty, so there (at least I guess)
// no possible way to have an NPE?
//return neg.max()
return neg.max()!! //this works fine
}
Is there any particular reason why compiler doesn't infer that .max() can only yield an Int, and thus this should not be an error, or am I missing something?
Kotlin's documentation proudly points out about Smart Casts, and I think this is a quite similar and easy inference to make?
That can’t be handled by smart casting, you're using a max() extension function which always returns a nullable type, Int? in your case:
public fun <T : Comparable<T>> Iterable<T>.max(): T?
The compiler does what this method signature suggests: it makes you handle the possible null. How should the compiler know wether max works as intended? It might be implemented wrongly.
The following, on the other hand, works thanks to smart casting:
val maxNeg: Int? = li.filter { it < 0 }.max()
if (maxNeg != null) {
return maxNeg //Can be used as Int
}
Related
I am trying to convert this piece of java code to kotlin
public int compare1(Comparable c, Object o) {
return c.compareTo(o);
}
to kotlin code:
fun compare1(c: Comparable<*>, o: Any?): Int {
return c.compareTo(o)
}
But get error
Type mismatch: inferred type is Any? but Nothing was expected
Any reason why this error occurs? Thanks
This code in Java shows a warning, because the compiler can't know if provided comparable can compare itself to provided object. Compiler still allows this, but it doesn't guarantee it won't throw an exception at runtime.
Its equivalent in Kotlin is either:
fun compare1(c: Comparable<Any?>, o: Any?): Int {
return c.compareTo(o)
}
With this code you'll have to do unchecked casts when calling the function. Or alternatively:
fun compare1(c: Comparable<*>, o: Any?): Int {
return (c as Comparable<Any?>).compareTo(o)
}
Note it doesn't solve the problem. You can call this function passing an integer and a string and then you will get an exception. So use this code only if the logic of your application guarantees you always pass matching objects to the function.
Even better, try to redesign your code to use generics in a type-safe manner. In that case your function would become:
fun <T> compare1(c: Comparable<T>, o: T): Int {
return c.compareTo(o)
}
This function is type-safe, so it doesn't allow using comparables with incorrect types. It may not work as a direct replacement of your Java function though, as the original function didn't care about the type safety. You may need to redesign other parts of your code to use this function.
Ideally, I think the commented-out if statements make more sense, at least to me. I mean, if someone asks me if null is bigger than 0, than I would answer no. Or if null is true, then also no. But those did not work as I expected, and throw compilation errors. So, I have changed those like below. But those do not look good or concise. Is there a better way to handle these?
class Result(val code:Int)
{
}
fun getResult():Result?
{
return null;
}
fun main(args: Array<String>)
{
var result = getResult();
var success:Boolean? = null;
//if(result?.code > 0)
if(result?.code?:0 > 0)
{
print("Good.");
}
//if(success)
if(success == true)
{
print("Good.");
}
}
The > is compiled to a call of compareTo(other: Int), which works by convention (its defined as an operator on Int). You cannot invoke this function on nullable Int? though.
There'd be a workaround: Create another extension on Int?:
operator fun Int?.compareTo(other: Int): Int =
(this ?: 0).compareTo(other)
Now your call does work:
if (result?.code > 0)
print("Good.")
the nullable element is actually the Result instance itself, not its code property.
i think using let in combination with the safe-get operator on the result more accurately reflects the design of the code:
result?.let {
if(it.code > 0) {
}
}
The reason the commented-out code did not compile is because result?.code is nullable and you can't call comparing operator (i.e > in this case) on a nullable expression because they don't type match. (it is defined to only accept non-nullable types)
if null is bigger than 0, than I would answer no. Or if null is true, then also no
Kotlin completely disambiguates between nullable and non-nullable entity. So, at compile time kotlin compiler refuses to compile if you are comparing something with a nullable type, to avoid run time exception ahead of time. In java,
Integer x = null;
System.out.println(x > `)
this compiles but throws a NullPointerException on runtime, which you obviously do not want. Kotlin compiler is just being smart here to avoid such runtime exceptions by refusing to compile.
Now onto the better way to deal with it, like everyone said, using let is an appropriate way. A simpler way can be a regular null check with if expresion
if(result != null && result.code > 0) {
print("Good.");
}
result?.code > 0
It's not working because > internally calls compareTo() method. compareTo() method does not work on a nullable object. That's why you have to add elvis operator(?:) on variable code to assign a value if the code variable is null. So that the value is not null and then compareTo() method works.
result?.code?:0 > 0
I have added a let to solve. If the result is not null then we will execute the code within the let block. Please check the updated code.
class Result(val code:Int){
}
fun getResult():Result?{
return null;
}
fun main(args: Array<String>){
var result = getResult();
var success:Boolean? = null;
//if(result?.code > 0)
if(result?.code?:0 > 0){
print("Good.");
}
// Updated Code
result?.let{
if(result.code > 0)
print("Good.")
}
//if(success)
if(success == true){
print("Good.");
}
}
When I try to cast Any to a List like in the example below I get 'Unchecked cast: Any! to List' warning. Are there any workarounds to this kind of problem?
val x: List<Apples> = objectOfTypeAny as List<Apples>
This is "just" a warning saying that it's not 100% safe just to cast. The better option would be:
if (objectOfTypeAny is List<*>) {
val a: List<Apples> = objectOfTypeAny.filterIsInstance<Apples>()
...
}
See https://kotlinlang.org/docs/reference/typecasts.html for details.
Except ignoring the warning (or improving the design to avoid the cast), no.
This warning means that the cast can succeed at runtime even though the list is not actually a List<Apples>, but contains something other than Apples.
It exists because generics are not reified in Java. Generics work with type erasure. they're a compile-time safety net, not a runtime safety net.
Solution case for android Serializable to ArrayList:
ModJsonAndDb - your class
private fun serializableToArrayList(obj: Serializable?): ArrayList<ModJsonAndDb>? {
return if (obj is ArrayList<*>) {
ArrayList(obj.filterIsInstance<ModJsonAndDb>())
} else {
null
}
}
1. Add:
inline fun <reified T> List<*>.asListOfType(): List<T>? =
if (all { it is T })
#Suppress("UNCHECKED_CAST")
this as List<T> else
null
2. Use:
val list: List<YouType> = someAnyList?.asListOfType<YouType>()
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.
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