Smart cast to 'Bitmap!' is impossible, because 'bitmap1' is a mutable property that could have been changed by this time - kotlin

bitmap1 = Bitmap.createScaledBitmap(
bitmap1, // <---- error is here
(width.toInt()),
(height.toInt()),
false)
numberOfInvaders ++
I also used bitmap2 and bitmap 1 in another class :
if (uhOrOh) {
canvas.drawBitmap(Invader.bitmap1, // <--- error is here
invader.position.left,
invader.position.top,
paint)
} else {
canvas.drawBitmap(Invader.bitmap2, // <---- and here
invader.position.left,
invader.position.top,
paint)
}
here its says : Type mismatch,
Required:Bitmap Found: Bitmap?

Yup, that's true :) You cannot use value like this, because it can be null at some point.
createScaledBitmap requires nonnullable Bitmap, but there is no guarantee that bitmap you use won't be null at the moment of calling given function.
So, what you can do?
Before the call check if bitmap is not null:
if (bitmap != null) { /* code here, still requires !! operator */ }
In multithreaded environment there is a risk that during execution of code block a value will change anyway, so you can use let function with ?. operator (basically the same operator like ., but executes only if value is not null). The block code will be invoked with an effectively final argument which is an instance you use to call this method, in this case "bitmap", called "context object", accessible via it keyword:
bitmap?.let { /* code here, bitmap is passed as effectively final, so for sure it's not null */ }
There other way would be !! operator (but it can finish with NPE exception, if value is null). Use only if you are sure that this value at that moment won't be null, otherwise you can crash your application.
Also, you can use ?: operator - this will take first value if not null, otherwise the second. It's quite nice, because you can use for example default value. Also, you can throw exception there ;)
bitmap ?: throw IllegalStateException("bitmap is null") // exception
bitmap ?: DEFAULT_BITMAP // default bitmap, if any
In this case you will get exception but with very communicative message (instead of just NPE).

bitmap1 = Bitmap.createScaledBitmap(
bitmap1!!, // !! <--- helps
(width.toInt()),
(height.toInt()),
false)
numberOfInvaders ++
if (uhOrOh) {
canvas.drawBitmap(Invader.bitmap1!!, // here
invader.position.left,
invader.position.top,
paint)
} else {
canvas.drawBitmap(Invader.bitmap2!!, // and here too
invader.position.left,
invader.position.top,
paint)
}

Related

Kotlin, remove duplicates from Linkedlist : smart cast is impossible

I implemented a LinkedList in kotlin and wrote a method to remove duplicates from it:
class Node (value:Int) {
var value = value
var next:Node? = null
fun addNodeToTail(value:Int){
var node = this
while (node.next != null) {
node = node.next
}
val newNode= Node(value)
node.next= newNode
}
fun removeDuplicates (){
val set = HashSet<Int>()
var node = this
set.add(node.value)
while(node.next != null){
if (set.contains(node.next?.value)){
node.next= node.next?.next
}else{
set.add(node.next.value)
node= node.next
}
}
}
}
In the last two lines:
set.add(node.next.value)
node= node.next
(and in the addNodeToTail method), the compiler says that smart cast is impossible because of complex expression. I have to add non-null asserted call (!!).
I want to understand why this solution is not accepted, although the while expression checks that node.next is not null. And I want to know if there is a better solution than using non-null asserted call (!!).
Thank you for your help
Pawel technically answered in the comment.
Basically smart casts are not always possible. In particular, if you define a mutable var of nullable type that is technically accessible by multiple threads, the compiler cannot guarantee that the value stays the same between the null check and the usage. That's why you get this error "smart cast impossible".
A common way of dealing with the problem is to store the value in a local val variable, to guarantee that this value will not change, and allow the compiler to smart cast it.
In your case though, it's not ideal because the while has to check the actual node's value every time. So you'll have to assert that the value is not null at some point, either with !! or with an elvis (?:) and an error() or throw.
I would personally go for:
while (node.next != null) {
val nextNode = node.next ?: throw ConcurrentModificationException()
if (set.contains(nextNode.value)) {
node.next = nextNode.next
} else {
set.add(nextNode.value)
node = nextNode
}
}

Declare val without initialization in function

class Solution {
val message: String //error : val must be initialized or abstract
message = "love" //error : val cannot be reassigned
}
I understand what's happening in here - val cannot be reassigned.
So when I need val but can not initialize it i used to use by lazy
class Solution {
fun love(){
val message : String
message = "love" //this works
message = "hate" //this is error "val cannot be reassigned"
}
}
Here I can delcare val without initialization and later write codemessage = "love".what's happening here?
#deHaar noticed correctly that only var (mutable variable) is appropriate in your case.
The error you get is absolutely correct and expected.
what's happening here?
When you declare a read-only variable without initializing it you have to make sure that each execution path will have a value in this read-only variable. It means that Kotlin makes sure if your read-only variable was or was not initialized in every place it is used and raises errors if the variable is used inappropriately.
Here you have only one execution path as there are no when or if statements that can split execution into several possible paths.
class Solution {
fun love(){
val message : String
message = "love" // Kotlin knows that `message` was not yet initialized
message = "hate" // Kotlin knows that `message` was yet initialized! It does not allow to modify the value.
}
}
Here is what Kotlin documentation says:
... it is also possible (but discouraged) to split the declaration and the initial assignment, and even to initialize in multiple places based on some condition. You can only read the variable at a point where the compiler can prove that every possible execution path will have initialized it. If you're creating a read-only variable in this way, you must also ensure that every possible execution path assigns to it exactly once.
Example of an execution path
Using when or if statement you create two or more execution paths. Execution paths can be presented as a graph, I'll use #number as a node number. Example:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #3
}
}
}
Looking at this example, that does not compile, we can calculate at least 3 execution paths.
#1 (none of the if statements was entered. All conditions are false)
#1 -> #2
#1 -> #3
Kotlin can calculate these paths and check if the message variable is initialized in every path it is used. As we can see, as soon as you reach the evaluation of the second if statement (in case of first and third paths) your program will crash because the message has no value. It has no address in memory and a computer which runs this program does not know how to get a value from an address that does not exist.
Now, let's modify this code to make it work:
class Solution {
fun love(){
// #1
val message : String
if (System.currentTimeMillisec() % 2 == 0) {
message = "Not empty"
// #2
} else {
message = ""
// #3
}
if (message.isEmpty) { // Error! Message could be not initialized at this point!
println("Empty message")
// #4
}
}
}
Execution paths:
#1 -> #2
#1 -> #3 -> #4
In this example, Kotlin is sure that the message read-only variable is initialized because there is a 100% chance that one of node 2 or node 3 will be executed. Right after the line where the message gets its initial value (initialized) Kotlin treats this variable as a read-only variable with a value.
Questions are welcome. I will try to simplify this answer.

Why kotlin insist `ret` variable to be initialized?

I have the following function:
override fun countForTicket(dbc: SQLiteDatabase, ticketId: Long): Int {
var ret: Int
dbc.query(
TABLE_SECOND_CHANCE_PRIZES, arrayOf("count(id)"),
"ticket = ?", arrayOf(ticketId.toString()),
null, null, null
).use { c ->
ret = if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
return ret
}
The problem is that in line return ret ret is underlined with red and when trying to compile it gives me error:
Variable 'ret' must be initialized
From my point of view it seems that ret is always initialized. What am I missing?
Is it because the initialization is happening in a lambda and the compiler cannot guarantee that the variable is initialized?
The compiler isn't smart enough to know for sure the lambda will be run once, so it can't figure this out for you.
The reason we don't have this problem with many of the standard library higher-order functions is that they utilize contracts, which tell the compiler what they are doing with the lambda that is passed in (such as guaranteeing that the lambda will be called exactly once).
Unfortunately, Closeable.use() doesn't specify a contract (possibly because of it re-throwing exceptions?).
But use does return the result of calling the lambda, so you could do
val ret = dbc.query(...).use { c ->
if (c.moveToFirst()) {
c.getInt(0)
} else {
0
}
}
The compiler don't allow unsafe variables like that to be returned. A variable must always be something.
In your case, ret is initialized inside a lambda. The compiler doesn't know if this lambda is executed or not. If not, ret remains in its unsafe state. Throwing a NullPointerException at the end.
If you're sure that this variable is always assigned you can look at lateinit variables. You can also put a default value to it var ret = 0 and ommit the else statement.

Simplify testing of a null variable in an IF statement

In Kotlin I have this (which will not compile):
var list: MutableList<String>? = null
if (list.isNotEmpty()) {
}
This will compile:
var list: MutableList<String>? = null
if (list!!.isNotEmpty()) {
}
However, if list is null, a runtime exception will occur. I could do this:
var list: MutableList<String>? = null
if ((list != null) && list.isNotEmpty()) {
}
But this seems to be repetitive everywhere you need to test if something is null. Is there a more eloquent way of doing this in Kotlin?
In the specific case of checking if the list is not null or empty you can use:
if (!list.isNullOrEmpty())
For a list, it's better to avoid handling null state instead handle only empty and non-empty state. refer http://thefinestartist.com/effective-java/43.
Saying that, we don't need to explicitly check for null check and only empty check alone should do the trick.
var list : MutableList<String> = mutableListOf()
list.add("Test1")
list.takeIf { it.isNotEmpty() }?.forEach { println(it) }
We can use
takeIf
to check whether the list is empty or not.
The first way add this line
list = list?:MutableList<String>()
Second way
val isEmpty = list.isEmpty()?:false
if(isEmpty){}else{}
Third way
if (!list.isNullOrEmpty())
as #AndroidDev suggested
Why are getting an error? Since !! require non-null. if the object is null then it will throw NPE
I think most can be done with the safe operator ?. itself. So if you just want to iterate over the list (or reduce, map, or whatever), you can just simply do so directly:
val sumExpression = list?.joinToString("+") { it.someValue } ?: throw exception? use default value?
list?.forEach { println("an entry in the list: $it") } // if there are entries, just iterate over them... if not, there is nothing to do
list?.also {
consume(it) // consume now is only called if the list is not null
}
Enhancing it then with a condition is also rather easy with takeIf or takeUnless (depending on what you require):
list?.takeIf { it.isNotEmpty() }
Now you still have a nullable list and can again call any of the desired functions as shown before:
list?.takeIf( it.isNotEmpty() }?.also {
consume(it)
}
Also a variant instead of list.isNullOrEmpty() (already shown by gpuntos answer) is to compare the possible null value to the expected outcome, e.g.:
if(list?.isNotEmpty() == true) {
// do something with the non-empty list
}
In case it is null the condition simplifies to basically null == true which is false. However I wouldn't overuse that too much as then you don't see the actual nullable types so easily anymore. Such a usage may make sense, if what you are trying to calculate isn't already supported in its own extension function (as is with isNullOrEmpty()), e.g. for entering the if only if the count is 4 it makes sense to use something like: if (list?.count() == 4)...

How to tell IDEA/Studio that the null check has been done?

I'm developing with Android Studio/IntelliJ IDEA.
I have enabled the inspection check called "Constant conditions & exceptions" that shows a warning if I am risking a NPE, such as:
String foo = foo.bar(); // Foo#bar() is #nullable
if (foo.contains("bar")) { // I'm living dangerously
...
}
I have the following in my code:
String encoding = contentEncoding == null ? null : contentEncoding.getValue();
if (!TextUtils.isEmpty(encoding) && encoding.equalsIgnoreCase("gzip")) {
inputStream = new GZIPInputStream(entity.getContent());
} else {
inputStream = entity.getContent();
}
Here's the source code of TextUtils#isEmpty(String):
/**
* Returns true if the string is null or 0-length.
* #param str the string to be examined
* #return true if str is null or zero length
*/
public static boolean isEmpty(CharSequence str) {
if (str == null || str.length() == 0)
return true;
else
return false;
}
I'm not risking any NPE because TextUtils#isEmpty(String) would return true to a null pointer.
However I'm still getting the little Method invocation 'encoding.equalsIgnoreCase("gzip")' may produce 'java.lang.NullPointerException' warning, which can be annoying.
Is it possible to make this check smarter and ignore the NPE warning if there's already a null-check done?
You can look into the link that Peter Gromov mention in his answer.
Created some simple classes that resemble your setup:
A class with a method annotated with #Nullable:
The TextUtil class with it's isEmpty method:
And finally the main class calling the TextUtil#isEmpty:
Now if you enter the File -> Settings... and go to Inspections ->Constant conditions & exceptions part you can change the Configure Assert/Check Methods to cater for your isEmpty method:
Add a new IsNull check method:
Enter the TextUtil class, isEmpty method and CharSequence parameter:
This gives this Assert/Check Method Configuration window:
Press Ok and then Ok again to go back to the editor view and you'll see that the inspection disappeared:
You are actually telling IntelliJ that the isEmpty method is doing a null check on the str parameter.
You could use //noinspection ConstantConditions that will remove the NPE warning for the following line, like this:
String encoding = contentEncoding == null ? null : contentEncoding.getValue();
//noinspection ConstantConditions
if (!TextUtils.isEmpty(encoding) && encoding.equalsIgnoreCase("gzip")) {
inputStream = new GZIPInputStream(entity.getContent());
} else {
inputStream = entity.getContent();
}
You can use #SuppressWarnings("ConstantConditions") annotation.
#SuppressWarnings("ConstantConditions")
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int indexViewType) {
if (inflater == null) {
inflater = LayoutInflater.from(parent.getContext());
}
ItemViewProvider provider = getProviderByIndex(indexViewType);
provider.adapter = MultiTypeAdapter.this;
return provider.onCreateViewHolder(inflater, parent);
}
Select "TextUtils.isEmpty".
Right Click -> Show Context Actions -> Add Method Contract.
Enter "null -> true".
Save the configuration xml.
Please check the details here
See http://www.jetbrains.com/idea/webhelp/configuring-check-assert-methods.html for IDEA 12.
In IDEA 13 EAP, you can add method contract: http://youtrack.jetbrains.com/issue/IDEA-93372
Unfortunately marked as "right answer" solution is of date. But I found equivalent for me solution.
The new versions of IDE work correctly with static methods. So the example from the question won't throw warning anymore.
TextUtils#isEmpty(String);
public static boolean isEmpty(CharSequence str) {
// your checks
}