confused about the null safety dart - dart

The argument type 'int?' can't be assigned to the parameter type 'num'.
when using darts with 'null safety', this code gets an error but when I use the version of darts without null safety the test runs successfully (without error). My question is how do I change this code so that it doesn't error on darts with null safety?

If you take a look at the documentation of the [] operator on Map:
V? operator [](
Object? key
)
V? here means that the type of the value V will always be nullable. The reason for this is because of the following detail for this operator:
The value for the given key, or null if key is not in the map.
https://api.dart.dev/stable/2.18.1/dart-core/Map/operator_get.html
So the reason for your error is because your code is not prepared for the case of [] returns null in case the key are not in your Map.
There are multiple solutions for this problem which depends on how sure you are about the specific piece of code and what behavior you want in case of failure.
If you are e.g. completely sure that curr will always point to a valid key in the Map you can do: scarScore[curr]! (notice the !). This will add a runtime null-check and crash your application in case the value from the [] operator ends up being null.
Alternative, if you expect some keys to not be in the Map, you can provide default values like prev + (scarScore[curr] ?? 0) where null will then be assumed to be translated to the value 0.
For a detailed explanation for why the [] operator are nullable, you can read: https://dart.dev/null-safety/understanding-null-safety#the-map-index-operator-is-nullable

Related

What has the difference between declaring a variable with var and int do with the Null Safety?

Why does Dart allow this:
void main() {
var A;
A ??= 12;
print(A);
}
The output is 12. But not this:
void main() {
int A;
A ??= 12;
print(A);
}
Here's the error:
lib/project_dart.dart:4:2: Warning: Operand of null-aware operation '??=' has type 'int' which excludes null.
A??= 12;
^
lib/project_dart.dart:4:2: Error: Non-nullable variable 'A' must be assigned before it can be used.
A??= 12;
^
lib/project_dart.dart:5:8: Error: Non-nullable variable 'A' must be assigned before it can be used.
print(A);
^
In this case I have to add the ? after int so it can work but in the previous case it works fine without it the question is WHY?
var tells the compiler "I want you to figure out what type this variable should be" based on the assignment. In this case, there is no assignment to the variable upon declaration, so there is no information for the compiler to use to infer the type. Thus, according to the compiler, the type of A should be dynamic, which could be literally anything, so it's given the default value of null.
int explicitly tells the compiler "I want this variable to be a non-nullable int". Non-nullable variables cannot be given a default value when they are declared without a value, so they must be assigned before they are referenced for the first time, which means you can't do print(A) before A has been definitely given a value.
A ??= 5; is less obvious why it's failing, but think about what it's doing. It checks if A is null, and if it isn't, it assigns it the value of 5. In order to check A, the program needs to reference A, and as we know, A can't be referenced because it's not nullable and hasn't been definitely assigned a value. Additionally, A ??= 5 is redundant because A is non-nullable and therefore can never be null.
Note that a declared variable that hasn't been assigned a value is not going to implicitly contain null. There is no check for whether a variable has been assigned yet, which is why you should be careful when declaring non-nullable variables without immediately assigning them a value. It's easy to find yourself in a race condition where code that will assign the variable may or may not occur before other code that will reference the variable.

Why Dart holds *double standard* between `==` and `>=`

I have two int values in Dart, test them equals or not with == previously, it was fine. Now I have to test if first one is greater or equals to the second one, so I changed == to >= instinctively, but it seems Dart was not happy with me doing that
I double checked documentation of Dart, >= should work to test greater or equals operation
IMO, in the end, if >= has something wrong, then the same should come to ==
You are comparing two different operators which comes from different part of the Dart language. The == operator is defined on Object:
https://api.dart.dev/stable/2.15.1/dart-core/Object/operator_equals.html
Which needs to be able to compare every type of object including null. The reason why the == operator does not have Object? as input parameter is because of the following detail in the Dart Language Specification (page 167) under the section "Equality":
Evaluation of an equality expression ee of the form e1 == e2 proceeds as follows:
The expression e1 is evaluated to an object o1.
The expression e2 is evaluated to an object o2.
If either o1 or o2 is the null object (17.4), then ee evaluates to true if both o1 and o2 are the null object and to false otherwise. Otherwise,
evaluation of ee is equivalent to the method invocation o1.==(o2)
...
https://dart.dev/guides/language/specifications/DartLangSpec-v2.10.pdf
So based on third rule, the language itself will make sure that null values are compared correctly and therefore the Object.== method will never see a null object as its input.
The other operator is >= and comes (in your case) from num which int extends from:
https://api.dart.dev/stable/2.15.1/dart-core/num/operator_greater_equal.html
And is specified to accept only num (so int or double) which are a non-nullable type (null is therefore not allowed).
You are then trying to call the num.>= operator with the result from values?.length. This is a problem since values is nullable and can therefore potentially be null. And when you use the ?. operator, you are saying that if values are null, don't care calling .lenght but instead just return null.
So the type of values?.length ends up being int? which is not compatible with num.

Pass an empty value to an OData V2 Edm.Time property

I have a variable type time but sometimes this variable doesn't have anything.
When it is initial, it shouldn't be "000000", I want an empty value without anything (no zeros). Let me explain my problem with the code:
IF lwa_hora IS INITIAL.
CLEAR lwa_hora.
ls_entity-hora = lwa_hora. " Result: 000000 but I don't want any zero
ELSE.
ls_entity-hora = lwa_hora. " Result: 000000
ENDIF.
I tried with CLEAR but nothing happens.
I need this is because in the JavaScript frontend client logic, I need the OData property to contain a falsy value (E.g. null or an empty string "").
But it always has the value "000000" which is not an empty string. Is it possible to make something in the backend to "clear" the variable?
The time data-type in abap (t) is a value-type. It's internally implemented as an integer counting the seconds since midnight. 0 seconds since midnight is a valid value, so it can't have a null-value.
However, ABAP allows you to create a reference to any value-type:
hora TYPE REF TO t.
That means hora will be a reference to a variable of TYPE t. Initially, this reference will be unbound, which is conceptually very similar to a null-reference in other programming languages. You can check that with:
IF ls_entity-hora IS BOUND.
...
IF ls_entity-hora IS NOT BOUND.
You can assign a time value with GET REFERENCE OF lwa_hora INTO ls_entity-hora. But now you have a reference to an existing variable. Change the value of lwa_hora, and the value of ls_entity-hora also changes. That might not always be what you want. So it might be better to create a new piece of data in memory for our reference to point to:
CREATE DATA ls_entity-hora.
Now ls_entity-hora is no longer unbound (or "null" if you want to call it that), it points to a new time-value of 000000. If you want to read or change the value of this nameless piece of data this reference points to, then you can do this with the dereferencing-operator ->*:
ls_entity-hora->* = lwa_hora.
If you intentionally want to set a reference to unbound (or "set the reference to null"), you can do that by clearing the reference:
CLEAR ls_entity-hora.
By the way: Representing a point in time by two separate variables of the types d and t fell out of fashion in the past decade. The current best practice for this situation is to use a single variable of type TIMESTAMP (if you need second precision) or TIMESTAMPL (if you need microsecond precision). A timestamp of 00000000000000 is obviously an illegal value, so it can be used to represent the absence of a point in time. This type also usually makes it much easier to communicate with a SAPUI5 frontend (like in your case), because many of the technologies for making oData services offer automatic conversion between Javascript Date and ABAP TIMESTAMP.
An alternative to heap allocating the time would be to store a boolean next to it, indicating whether it is set or not:
TYPES:
BEGIN OF optional_time,
time TYPE t,
is_null TYPE abap_bool,
END OF optional_time.
DATA(no_time) = VALUE optional_time( is_null = abap_true ).
" Setting to null:
DATA(some_time) = no_time.
" Setting to some time:
some_time = VALUE #( time = '12:30' ).
IF some_time = no_time.
" ...
ENDIF.
This sort of things is probably better to handle on front-end than on back-end.
SAP Gateway serializes ABAP Date/time initial values to NULL in the OData response if the corresponding property is nullable. You need to make sure this property is set to TRUE like in this sample
you can also set this property in runtime
TRY .
lo_action = model->get_entity_type( iv_entity_name = 'Z_REPORTType').
lo_property = lo_action->get_property( iv_property_name = 'Requested_Date').
lo_property->set_nullable( iv_nullable = abap_true ).
CATCH /iwbep/cx_mgw_busi_exception /iwbep/cx_mgw_med_exception /iwbep/cx_mgw_tech_exception INTO DATA(lo_exception).
ENDTRY.

Clarification on avoid_null_checks_in_equality_operators linter rule

There is a linter rule which verifies that one don't check for null equality in the overridden == operator.
The rule is here.
I understand this rule but can't see how it is realized technically.
It seems that Dart itself makes some implicit check on other != null and == returns false in this case. Is this correct?
In other languages, e.g. Java, one needs to explicitly add this check in the overridden equals.
Second question is why then it does not check automatically on the type of other as well. Why it is ok to spare me as a programmer from checking on null, but I still need to check if other is Person? Are there cases when one overrides == and checks there for some other type then the type of that class?
The linter rule implementation is simple, it just checks whether you compare the argument of operator == to null.
The reason you don't need to is that e1 == e2 in Dart is defined to first evaluate e1 and e2 to a value, then give a result early if one or both of the values is null, and only otherwise it calls the operator== method on the value of e1.
So, when that method is called, you know for certain that the argument is not null.
The reason the == operator doesn't do more checks before calling the operator== method is that there are examples where that would be wrong.
In particular, int and double can be equal to each other. Having instances of different classes potentially being equal to each other is more common that you'd think (proxies, mocks, wrappers, Cartesian point vs polar point, int vs double).
The other check you could potentially do early is to say that an object is equal to itself, so if (identical(this, other)) return true;, but there is one counter-example forced upon the language: NaN, aka. double.nan. That particular "value" is not equal to itself (which breaks the reflexivity requirement for ==, but is specified that way by the IEEE-754 standard which is what the CPUs implement natively).
If not for NaN, the language would probably have checked for identity before calling operator== too.
It seems that Dart itself makes some implicit check on other != null and == returns false in this case. Is this correct?
Yes.
Second question is why then it does not check automatically on the type of other as well. Why it is ok to spare me as a programmer from checking on null, but I still need to check if other is Person? Are there cases when one overrides == and checks there for some other type then the type of that class?
It's less common, but there can be cases where you might want to allow a different type on the right-hand-side of the equality. For example, the left-hand-side and right-hand-side might be easily convertible to each other or to a common type. Imagine that you created, say, a Complex number class and that you wanted Complex(real: 4.0, imaginary: 0.0) == 4 to be true.
From the doc:
no class can be equivalent to [null]
Meaning, when other is Person is true, then other != null is also true. This is because:
The null object is the sole instance of the built-in class Null.
(https://dart.dev/guides/language/spec)
So every instance check against any type but Null will return false for null:
class A {}
void main() {
final x = null;
final a = A();
print(x is Null); // true
print(x is A); // false
print(a is Null); // false
print(a is A); // true
}

Null-aware operator with Lists

In Dart the null-aware operator for methods in combination with the ?? operator does not work well.
Image having a call chain like this:
object.getter.getter.getter.getter
Now when object could be null and I do not want an exception I have to write this:
object?.getter?.getter?.getter?.getter
That is a null check for every getter, even though only object can be null, but then the getter's obviously do not work.
I am okay with this. But now I have a scenario, where I want to return another value if null, but I am working with a list:
list[index][index][index]
How do I use the null-aware operator on the List getter?
This does not work:
list?[index]
and this does not exist for List's in Dart.
list?.get(index)
I want to achieve something like this:
list?[index]?[index]?[index] ?? 0
Where I know that only list will ever be null.
This long code would work:
list == null ? 0 : list[index][index][index]
There is no null-aware operator for the indexing operator ([])
You can use elementAt() instead:
list?.elementAt(index)?.elementAt(index)?.elementAt(index) ?? 0
UPDATE - 2021/05/24
If you are using null-safety, you can now use this:
list?[index]?[index]?[index] ?? 0

Resources