So I have a class Cache which has a method compare that returns a bool.
I have an instance of this class that is nullable.
Cache? recent;
I want to executed a piece of code when recent is not null and compare returns false
Without null safety I would have just done
if(recent!=null && !recent.compare()){
//code
}
How to do the same with null safety enabled?
When I try above with null safety I get
The method 'compare' can't be unconditionally invoked because the receiver can be 'null'.
Try making the call conditional (using '?.') or adding a null check to the target ('!')
When I try below
if(!recent?.compare()){
//code
}
It gives me
A nullable expression can't be used as a condition.
Try checking that the value isn't 'null' before using it as a condition.
You can solve this problem either by
Using local variable (recommended)
var r = recent; // Local variable
if (r != null && !r.compare()) {
// ...
}
Using Bang operator (!)
if (!recent!.compare()) {
// ...
}
Related
I have problem working with null safety in dart.
void main() {
List<String>? testlist;
testlist?.add('null test');
print(testlist);
}
Why the printed testlist is still null?
Your testlist is still null, because you defined it as nullable and did not initialize it.
So when it comes to adding an entry to your testlist, while calling testlist?.add('null test');. A check will be performed, if testlist is null. If it is null, the call for add will be skipped. And in your case, it is always null.
So to visualize the null-conditional operator in your code line testlist?.add('null test');. It could also be written as:
List<String>? testlist;
// testlist?.add('null test');
if(testlist != null) {
testlist.add('null test');
}
print(testlist);
If you want to add the string, you have to initialize your testlist:
List<String>? testlist = [];
Or if possible you could avoid the nullable and write your code as:
List<String> testlist = [];
testlist.add('null test');
print(testlist);
Because you haven't assigned a value to testlist, it's null and the add method is not executed.
The add method simply "adds" an entry to the list, but if the list is null you can't add anything to it.
You have to initialize it somehow, for example as List<String>? testlist = [];, but it depends how you'll use it in the end.
Is there is a method in dart to judge whether an object is null or not, and then decide to get ['data'] or do nothing?
This is the error message:
The following NoSuchMethodError was thrown building Builder:
The method '[]' was called on null.
Receiver: null
Tried calling:
The simplest way to answer your question:
final data = list != null ? list[0] : null;
There is a shorthand method to do the same with properties and methods of any object : a?.b or a?.b() would first null check a and then get b or call b respectively, if a is null return null.
Such shorthand is not available for subscript only for properties and methods.
Prefer Null aware index operator:
var item = yourList?[0] ?? defaultValue;
I encountered something similar to this in a codebase.
if varA !== varB {
// some code here...
}
Is that the same as
if varA! == varB {
// some code here...
}
which means that varA is force unwrapped?
In swift == means "Are these objects equal?". While === means "Are these objects the same object?".
The first is a value equality check. The second is a pointer equality check.
The negation of these are != and !== respectively.
In Swift, === and !== are identity operators. They are used to determine if two objects refer to the same object instance.
So in that sample code, the condition is checking if varA and varB refer (or, rather, don't refer) to the same object instance even though they're different variables.
Does Dart support == and === ? What is the difference between equality and identity?
Dart supports == for equality and identical(a, b) for identity. Dart no longer supports the === syntax.
Use == for equality when you want to check if two objects are "equal". You can implement the == method in your class to define what equality means. For example:
class Person {
String ssn;
String name;
Person(this.ssn, this.name);
// Define that two persons are equal if their SSNs are equal
bool operator ==(other) {
return (other is Person && other.ssn == ssn);
}
}
main() {
var bob = Person('111', 'Bob');
var robert = Person('111', 'Robert');
print(bob == robert); // true
print(identical(bob, robert)); // false, because these are two different instances
}
Note that the semantics of a == b are:
If either a or b are null, return identical(a, b)
Otherwise, return a.==(b)
Use identical(a, b) to check if two variables reference the same instance. The identical function is a top-level function found in dart:core.
It should be noted that the use of the identical function in dart has some caveats as mentioned by this github issue comment:
The specification has been updated to treat identical between doubles
like this:
The identical() function is the predefined dart function that returns
true iff its two arguments are either:
The same object.
Of type int and have the same numeric value.
Of type double, are not NaNs, and have the same numeric value.
What this entails is that even though everything in dart is an object, and f and g are different objects, the following prints true.
int f = 99;
int g = 99;
print(identical(f, g));
because ints are identical by their value, not reference.
So to answer your question, == is used to identify if two objects have the same value, but the identical is used to test for referential equality except in the case of double and int as identified by the excerpt above.
See: equality-and-relational-operators
As DART is said to be related to javascript, where the === exists, I wish not be downvoted very quickly.
Identity as a concept means that 1 equals 1, but 1.0 doesn't equal 1, nor does false equal 0, nor does "2" equal 2, even though each one evaluates to each other and 1==1.0 returns true.
It should be noted that in Dart, identical works similarly to Javascript, where (5.0 == 5) is true, but identical(5.0, 5) is false.
In a Grails application, I am trying to prevent the creation of cycles in a directed graph. The user is able to assign a parent to a node, but no node should be its own parent's ancestor. I have written a simple setup function that calls checkLineageForTarget, which is the recursive function that does the heavy lifting:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
// true means that this is a safe addition
// false means that this addition creates a cycle
boolean retVal = stillToProcess.each {
Collection<Node> itsParents = getParentNodes(it)
if (it.id == target){
println("found a loop on " + target);
return false; // loop detected!
}
if (itsParents.empty){ return true; } // end of the line
return checkLineageForTarget(target, itsParents)
}
// at this point, retVal is always true, even when the "found a loop [...]" condition is met
return retVal;
}
This "works," in that it prints the "found a loop [...]" message, but outside of the closure, retVal is true, the calling function attempts to add the new parent/child relationship, and my stack runneth over.
What is my misunderstanding?
The each method returns the same collection it was invoked on, so retVal is probably not the boolean "true", but is evaluated as "truthly" (as it is a collection, it would mean it's not empty).
If you want to check a condition for every element in a collection, you might use every.
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
stillToProcess.every { node ->
node.id != target && checkLineageForTarget(target, getParentNodes(node))
}
}
Note that I didn't need check the .empty condition on the parent nodes collection because that will be filtered by the recursive call to checkLineageForTarget (i.e. calling .every on an empty collection always returns true). Also, because of the short-circuiting of the && operator, the iteration stops as soon as node.id == target :)
.each appears to return the Object being looped over when it is done. You are assign this to a boolean and it is being coerced to true. You probably want to use .every for your task. It returns true only if each iteration returns true and it will stop looping when it hits the first false. You can find more information in the groovy docs.
When you return inside a Closure, it's like returning inside a method call within the method - it's local to that scope and has no impact on the real method the closure is being called in. In this case you can use one of the other approaches suggested (e.g. every) or use a regular for loop since it works the same as the Groovy each (i.e. it's null-safe and supports but doesn't require types) but you can break out of the loop or return and since you're in a real for loop it will return from the method:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
for (Node node in stillToProcess) {
Collection<Node> itsParents = getParentNodes(node)
...
}
...
}