var b:Boolean = condition1() && condition2();
In this statement, if condition1() evaluates to false, will condition2() be evaluated?
Yes. The && operator (and also ||) is short-circuited.
This has been explained in http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/operators.html#logical_AND.
Returns expression1 if it is false or can be converted to false, and expression2 otherwise. Examples of values that can be converted to false are 0, NaN, null, and undefined. If you use a function call as expression2, the function is not called if expression1 evaluates to false.
A little googling indicates yes. Also, the usual term is short-circuited, rather than lazy.
Correct. The compiler should always take the quickest path out of a conditional. If any test case fails, the compiler knows that the results of further tests are irrelevant and decides to bail right then and there.
var obj:Object = null;
if( obj && obj.name == "fred" )
{
trace( "Hi, fred." );
}
You will never see "Hi, fred." because Obj is null. Good thing too, because the second test case will throw an error since you cannot dereference a null object in that manner. I've used this particular approach many times when I need to make sure some object is not null before dereferencing it.
Related
Can someone simply explain to me how null assertion (!) works and when to use it?
The ! operator can be used after any expression, e!.
That evaluates the expression e to value v, then checks whether v is the null value. If it is null, an error is thrown. If not, then e! also evaluates to v.
The static type of an expression e! is (basically) the static type of e with any trailing ?s remove. So, if e has type int?, the type of e! is int.
You should not use e! unless e can be null (the type of e is potentially nullable).
The ! operator is dynamically checked. It can throw at runtime, and there is no static check which can guarantee that it won't. It's like using a value with type dynamic in that all the responsibility of preventing it from throwing is on the author, the compiler can't help you, and you need good tests to ensure that it won't throw when it's not supposed to.
It's called an assertion because it should never throw in production code.
So, use e! when you know (for some reason not obvious to the compiler, perhaps because of some invariant guaranteeing that the value is not null while something else is true) that e is not null.
Example:
abstract class Box<T extends Object> {
bool hasValue;
T? get value;
}
...
Box<int> box = ...;
if (box.hasValue) {
var value = box.value!;
... use value ...
}
If you are repeatedly using ! on the same expression, do consider whether it's more efficient to read it into a local variable just once.
Also, if (like this Box example) the value being null is equivalent to the other test you just did, maybe just check that directly:
Box<int> box = ...;
var value = box.value;
if (value != null) {
... use value ...
}
This code, with an explicit != null check on a local variable, is statically guaranteed to not throw because the value is null.
The code using ! above relies on the author to maintain whichever invariant allowed them to write the !, and if something changes, the code might just start throwing at runtime. You can't tell whether it's safe just by looking at the code locally.
Use ! sparingly, just like the dynamic type and late declarations, because they're ways to side-step the compiler's static checking and ensure it that "this is going to be fine". That's a great feature when you need it, but it's a risk if you use it unnecessarily.
Future<bool?> f() async => true;
void main() async {
final proceed = await f();
if (proceed) {...} // Error
if (proceed != null && proceed) {...} // Works but too verbose
}
Is there any shorthand or a better way of proceeding instead of writing a long proceed != null && proceed check? I know I might be missing something so trivial.
Note: I know a default value can be provided to proceed but that's just not my use case. Bang operator can do the trick iff bool is actually not null.
Sounds like a case where you can use the ?? operator which are documented here in the language tour:
https://dart.dev/guides/language/language-tour#conditional-expressions
It will allow you to rewrite your long if-statement into:
if (proceed ?? false) {...}
?? means here that we want to use the value of proceed as long as it is not null. In case of null we want to use false as the value.
One drawback of this is that it does not seem like Dart makes any type promotion of proceed compared to the following where proceed afterwards is promoted to a non-nullable type:
if (proceed != null && proceed) {...}
Not sure if there are a Dart issue on that.
void main() {
int? foo;
var isNonNull = foo != null;
if (isNonNull) foo.isEven; // Error
}
I already did a check on foo and stored its value in isNonNull variable. I could understand that warning if the scope wasn't local.
Note: I know I can use ! bang operator to resolve it but why flow analysis isn't working?
Dart type promotion is based on a check of a variable going a particular way dominating a later use of that variable.
So, if you do if (x != null) x.foo();, it detects that the check x != null being true means that the later x.foo() is valid. That only works because the compiler can also convince itself that the variable's value doesn't change between the check and the use.
If you introduce an extra boolean variable, like here, then the check is no longer performed inside the branch. That doesn't make it impossible to remember that the isNonNull boolean value being true means that foo is non-null, but it gets more complicated. The compiler now has to ensure that foo doesn't change and that isNotNull doesn't change.
The Dart compiler bails out instead, it doesn't track that level of complication. It only tracks promotion through one variable at a time.
You get the error because flow analysis can't know if isNonNull is ever going to get a new value. Take this example:
final random = Random();
void main() {
int? foo;
var isNonNull = foo != null;
isNonNull = random.nextBool();
if (isNonNull) foo.isEven;
}
So, its better to use
if (foo != null) foo.isEven;
I am using Dart with null-safity mode. I have porblem in next code.
List<JobModel> _jobList = [];
// ...
if(_jobList.isNotEmpty) {
for(var job in _jobList) {
if(job.cmd_args != null) {
numbersOfAlreadyActiveJobs.addAll(job.cmd_args.replaceAll('(', '').replaceAll(')','').split('=')[1].split(',').map(int.parse).toList());
}
}
I
am getting next error:
But why? I am doing check of job.cmd_args != null
Dart promotes local variables only, this goes both for types and null-ness.
When you do if (job.cmd_args != null) ..., it doesn't allow the compiler to assume that any later evaluation of job.cmd_args will also return a non-null value. It cannot know that without knowing the entire program (to ensure that nothing implements a JobModel where cmd_args might be a getter which does something different on the next call). Even if it knows that, we don't want local type promotion to depend on global information. That makes it too fragile and you can't depend on it. Someone adding a class somewhere else might make your code no longer promote, and therefore no longer compile.
So, your test is not enough.
What you can do is either say "I know what I'm doing" and add a ! to job.cmd_args!.replaceAll. That's a run-time cast to a non-nullable type, and it will throw if the value ends up actually being null on the second read (which it probably won't, but compilers aren't satisfied by "probably"s).
Alternatively, you can introduce a local variable, which can be promoted to non-null:
var args = job.cmd_args;
if (args != null) {
numberOfAlreadyActiveJobs.addAll(args.replaceAll(...));
}
Here you introduce a new local variable, args, and then you check whether that is null. If it isn't, then you can safely use it as non-null later because a local variable can't be affected by code anywhere else, so the compiler believes it really will have the same value when you use it later.
I'm almost certainly doing something wrong or misunderstanding List.retainAll().
I have two lists that are equal (size() ==1), yet when I call list1.retainAll(list2), list 1 becomes empty.
Code:
List<DomainObject> list1 = someService.getData()
List<DomainObject> list2 = someService.getOtherData()
log.info("Equal: ${list1.equals(list2)}")
boolean changed = list1.retainAll(list2)
log.info("Changed: ${changed}")
log.info("list1 empty: ${list1.isEmpty()}")
Log shows:
Equal: true
Changed: true
list1 empty: true
I... don't know what I'm doing wrong. I've run tests with more generic objects (the actual domain object is fairly complex, but implements equals/hashcode correctly) and retainAll worked as expected.
I'm not even sure what specific question I should be asking... Are there conditions I'm missing where equals() can return true but retainAll() fails?
Hopefully to help someone else down the line, and this is something I should have known and had run into before.
list1.equals(list2) //true, one element per list
list1.get(0).id == list2.get(0).id //true, same DB object
list1.get(0).equals(list2.get(0)) //false... ?!
The reason was equals() was implemented starting with:
equals() {
if(getClass() != o.class) return false
}
getClass() was returning DomainObject but o.class (or o.getClass()) was returning javaassist__blahblah.
I replaced the check with
if(!(o instanceof DomainObject)) return false
Not sure if that's the cleanest way to do class checking in equals, for some reason I was of the impression that instanceof was slow.