Unnecessary usage of bang operator - flutter

I have a problem understanding, and living with, the excessive use of bang operators in dart/flutter.
Consider this example:
if(model != null && model!.someValue != null) {
print(model!.someValue!);
}
The first condition check is verifying that the model is not null. In the second condition I have to put in a bang operator after model, else the compiler gives me an The property 'someValue' can't be unconditionally accessed because the receiver can be 'null' error. But why is this necessary? I´ve just checked the variable! And same goes for the print(model!.someValue!); line.
Another example where I have these classes:
class GeoPosition {
double lat = 0;
}
class Wrapper {
GeoPosition? position;
}
...
Wrapper wrapper = Wrapper();
wrapper.position = GeoPosition();
wrapper.position!.lat = 1;
Now why do I need to put this bang operator (or ? operator) after position? I´ve just created a new instance of GeoPosition in the Wrapper instance - position cannot be null.
My best guess is that the compiler cannot see or understand the current context of the class. But in Typescript the linter is smart enough to know when these operators are not necessary.
I know that I can create local variables from the properties that I am trying to access, but this would be just as ugly ;)
So why are ! and ? necessary in these (and many other) situations? And is there anything I can do about it?

This is what happens with nullable properties (hence the message you get). It is explained here: Understanding null safety: Working with nullable fields.
You should be able to work around this issue by declaring the field as late, as in
class Wrapper {
late GeoPosition position;
}
Wrapper wrapper = Wrapper();
wrapper.position = GeoPosition();
wrapper.position.lat = 1;
This will remove the need to add a bang to every access to position The compiler will add a non-null check at appropriate places. Of course, the program will fail if you don't assign a non-null value before accessing the field.
If explained in Late variables

Nullable properties of a class can still be null between two access.
For your first example you can extract the variable then check it:
var someValue = model?.someValue;
if(someValue != null) {
print(someValue); // not null
}
For your second example an elegant way will be the .. operator :
Wrapper wrapper = Wrapper();
wrapper. Position = GeoPosition()..lat = 1;
If you want the GeoPosition to be not null you have to make it not nullable:
class Wrapper {
GeoPosition position;
Wrapper(this.position);
}
You can set the position field final to be immutable.
Be careful with the late keyword, if you forget to init the field you've got a crash like the ! operator.

Related

Syntactic sugar for calling a function with a nullable value that might return null

I am looking for syntactic sugar with null-safe-operators to do this:
map["key"] == null ? default : (int.tryParse(map["key"]!) ?? default)
This however accesses map twice and requires default twice.
Better, but verbose code that also compiles:
String? val = map["key"];
int? res;
if (val != null) { res = int.tryParse(val) }
res ??= default;
In essence I am looking for a way to only call a function if the parameter is not null. E.g. in kotlin you could do (pseudocode)
map["key"].let{ int.tryParse(it) } ...
I found this related question, however writing helper methods is even more verbose and I cannot edit the function to take nullable parameters.
What i would love to see for dart (but afaik this does not exist):
int.tryParse(map["key"]?) ?? default;
I.e. func(null?) => null, no matter what func is, or sth similar.
Is there a smooth way to do that or is the verbose way for now the "accepted" way?
EDIT
I am aware of the int.tryParse("") or int.tryparse(default.toString() hacks, however both are somewhat naughty as they will call tryParse either way instead of skipping it if the value is null anyways. (imagine replacing tryParse with a very expensive factory method)
There is two things you can do:
int.parse(map["key"] ?? default.toString());
This would work if you are sure that map["key"] can be parsed if it is not null.
If you have to do this operation a lot you can also write your own extension function and then use the null-safe operator:
extension NullSaveParser on String {
int? tryToInt() {
return int.tryParse(this);
}
}
And then use this:
map["key"]?.tryToInt() ?? default;
This extension is neccessary because there is currently no way in dart to not call a method if an argument would be null, only if the object that you are calling it on is null can be caught.
I hope this helps.
It's sort of a hack and maybe ugly but for this case you might want to write something like
int.tryParse(map["key"] ?? '') ?? default
That is, put a fallback value inside the tryParse that you know will make the tryParse return null

Flutter null-safety conditionals in object methods

I'm just working through this whole null-safety mode with my Flutter project and unsure what the difference is with ? and ! in calls to object methods.
For example, the hint was to add a ! conditional. Here's an example I have right now, and I'm unsure if this should be a ? or a ! at the findNbr!.replaceAll().
Future checkItem({String? findNbr}) async {
int? x = int.tryParse(findNbr!.replaceAll('-', ''));
...
Does this mean replaceAll() will not run if findNbr is null?
Or should it be a ? instead? findNbr?.replaceAll()
EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.
Or does it mean I say it's not null and run it anyway?
For your information, I have not come close to running my app yet so I have no idea if it even works. But I figure I better know what it's doing before get too much more done. I'm still in the process of converting everything and there's 75-100 dart files. I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.
Future checkItem({String? findNbr}) async {
int? x = int.tryParse(findNbr!.replaceAll('-', ''));
...
Does this mean replaceAll() will not run if findNbr is null?
Correct. If findNbr is null, then findNbr! will throw a runtime exception. That would be bad, especially since checkItem's function signature advertises that findNbr is allowed to be null, and therefore it would violate callers' expectations.
Or should it be a ? instead? findNbr?.replaceAll()
EDIT: I just noticed I cannot use findNbr?, it's telling String? can't be assigned parameter String.
You can't use findNbr?.replaceAll(...) because if findNbr is null, then it would be invoking int.tryParse(null), but int.tryParse is not allowed to take a null argument.
What you need to do is one of:
Make findNbr no longer optional:
Future checkItem({required String findNbr}) async {
int? x = int.tryParse(findNbr.replaceAll('-', ''));
...
Allow findNbr to be optional but have a non-null default value:
Future checkItem({String findNbr = ''}) async {
int? x = int.tryParse(findNbr.replaceAll('-', ''));
...
Allow findNbr to be optional but explicitly decide what to do if it is null. For example:
Future checkItem({String? findNbr}) async {
int? x = findNbr == null ? null : int.tryParse(findNbr.replaceAll('-', ''));
...
I'm not sure I get the point of it all to be honest, because I just add ? to everything, so its all nullable anyway.
If you blindly add ? to all types and add ! to all variables, then yes, null-safety would be pointless: doing that would give you the same behavior as Dart before null-safety.
The point of null-safety is to prevent things that shouldn't be null from ever being null. You could have written such code before, but without null-safety, that meant performing runtime null checks (e.g. assert(x != null);, if (x != null) { ... }, or relying on a null-pointer-exception to crash the program if null was used where it wasn't expected). Null-safety means that such checks now can be done at build-time by static analysis, which means that errors can be caught earlier and more completely. Furthermore, whereas previously functions needed to explicitly document whether arguments and return values were allowed to be null (and inadequate or incorrect documentation could be a source of errors), now they're self-documenting in that regard. It's just like using int foo(String s) versus dynamic foo(dynamic s); using strong types catches errors earlier and better describes the function's contract.
I recommend reading Understanding Null Safety if you haven't already done so.
I would like to advice you to use the ! operator, also the called bang operator, as little as possible. You should only use this operator when the dart analyser is wrong and you know for 100% that the value will never be null.
Below is an example of where the dart analyser would be wrong and you should use the bang operator.
// We have a class dog with a nullable name.
class Dog {
String? name;
Dog({this.name});
}
void main() {
// We create a dog without a name.
final dog = Dog();
// We assign the dog a name.
dog.name = 'George';
// The dart analyser will show an error because it can't know if the
// name of the object is not null.
//
// Will throw: `A value of type 'String?' can't be assigned to a
// variable of type 'String'`.
String myDogsName = dog.name;
// To avoid this, you should use the bang operator because you `know` it
// is not null.
String myDogsName = dog.name!;
}
The ? operator simply tells Dart that the value can be null. So every time you want to place a ? operator, ask yourself, can this value ever be null?
The null safety features in Dart are mainly created for helping the developer remember when a value can be null. Dart will now simply tell you when you made a variable nullable in order to force null checks or default values for example.

A way to read a String as dart code inside flutter?

I want to build a method to dynamically save attributes on a specific object
given the attribute name and the value to save I call the "save()" function to update the global targetObj
var targetObj = targetClass();
save(String attribute, String value){
targetObj.attribute = value;
print(targetObj.attribute);
}
But I'm getting the following error:
Class 'targetClass' has no instance setter 'attribute='.
Receiver: Instance of 'targetClass'
Tried calling: attribute="Foo"
The only thing that I can think of is that "attribute" due to being type String results in an error.
That lead me to think if there is a way to read a String as code, something like eval for php.
As #Randal mentioned, you cannot create class..method at runtime. Still, you can try something like this.
A certain class
class Foo {
dynamic bar1;
dynamic bar2;
// ...
}
Your save method
save(Foo fooObject, String attribute, dynamic value) {
if ("bar1" == attribute) fooObject.bar1 = value;
else if ("bar2" == attribute) fooObject.bar2 == value;
// ...
}
Dart (and thus flutter) does not have a way to compile and execute code at runtime (other than dart:mirrors, which is deprecated). You can build additional code that derives from other code using the various builder mechanisms, although it can be rather complicated to implement (and use!).

How to use optional in Dart for null safety

I found this package that implements Optional for Dart: https://pub.dev/packages/optional/example
On the examples, it does things like this:
void filterExample() {
final hello = Optional.of('hello');
final world = Optional.of('world');
final name = Optional.of('harry');
for (var o in [hello, world, name]) {
final filtered = o.filter((v) => v.startsWith('h'));
print(filtered.isPresent);
} // prints "true", "false", "true"
}
But how do I force a variable to be Optional of some type? I wanted to have Optional<String>, Optional<int>, etc, but I'm forced to give a value in the beginning.
The closest I can think is
final anEmpty = Optional.ofNullable(null);
which is already in the example, but what is a Nullable? If I do like this, I cannot constraint the value to be a String or int, it can be changed to anything. I want to stick to strong typing while using Optional.
If this is not possible with this library, then how can I make my own simple Optional type that supports templates so I can have Optional<String>, Optional<int>, etc?
The behavior you are looking for (doing something if null and something else otherwise) can be achieved using the ?? operator.
This is basically a null check which equals the left hand if not null and right hand if null. Example: doSomething(myString ?? "Default value of my string");
Does this answer your question? Sorry, I haven't used that package and don't see a need to.

Is it bad practice to have my getter method change the stored value?

Is it bad practice to change my getter method like version 2 in my class.
Version 1:
public String getMyValue(){
return this.myValue
}
Version 2:
public String getMyValue(){
if(this.myValue == null || this.myValue.isEmpty()){
this.myValue = "N/A";
}
return this.myValue;
}
I think it is actually quite a bad practice if your getter methods change the internal state of the object.
To achieve the same I would suggest just returning the "N/A".
Generally speaking this internal field might be used in other places (internally) for which you don't need to use the getter method. So in the end, the call to foo.getMyValue() could actually change the behaviour of foo.
Alternatively, the translation from null to "N/A" could be done in the setter, i.e. the internal value could be set to "N/A" if null is passed.
A general remark:
I would only add states such as "N/A" if they are expected by some API or other instance relying on your code. If that is not the case you should rely on the standard null types that are available to you in your programming language.
In my opinion, unless you are doing lazy-loading (which you are not in that case), getters should not change the value. So I would either:
Put the change in the setter
public void setMyValue(String value) {
if(value == null || value.isEmpty()){
this.myValue = "N/A";
} else {
this.myValue = value;
}
}
Or make the getter return a default value if value not set properly:
public String getMyValue() {
if(this.myvalue == null || this.myvalue.isEmpty()){
return "N/A";
}
return this.myValue;
}
In the case of lazy-loading, where I would say that changing your members in a getter is fine, you would do something like:
public String getMyValue() {
if (this.myvalue == null) {
this.myvalue = loadMyValue();
}
return this.myValue;
}
No. You're doing two things here. Getting and setting.
Yes. It's a bad practice.
Why?
When the value is set (in a constructor or setter method), it should be validated, not when a getter method is called. Creating a private validate* method for this is also a good idea.
private boolean validateThisValue(String a) {
return this.myValue != null && !this.myValue.isEmpty();
}
public void setThisValue(String a) {
if (validateThisValue(a)) {
this.myValue = a;
}
else {
// do something else
// in this example will be
this.myValue = "N/A";
}
}
And, in the getter method, never ever change the state of the object. I have worked on some projects, and the getter often must be made const: "this method cannot change internal state".
At least, if you do not want to complicate things, in the getter method, you should return "N/A" rather than change internal state and set myValue to "N/A".
I usually define a specific getter.
Never alter original getter:
public String getMyValue(){
return this.myValue
}
And create a specific getter:
public String getMyValueFormatted(){
if(this.myvalue == null || this.myvalue.isEmpty()){
return "N/A";
}else{
return this.myValue;
}
}
I think it's better to initialize this.myValue = "N/A". And subsequent calls to setMyValue should modify the this.myValue according to your business conditions.
The getMyValue shouldn't modify in any way this.myValue. If your needs are to return a certain value, you should return that value (like "N/A") and not alter this.myValue . Getters must not modify member's value.
I would change better the setter method so, if the value is null or empty, the N/A is assigned to the attribute. So, if you use the attribute in other methods inside the class (v.g. toString()) you will have the intended value there.
Alternatively, change the setter method to launch an exception when the value being set is not right, so the programmer is forced to improve its handling prior to setting the value.
Other than that, it is ok.
I do feel this is a bad practice unless and until you explain the reason why it is so necessary for you modify the object inside the getter method instead of doing it inside the setter method.
Do you feel this cannot be done for some reason? Could you please elaborate?
Do what ever you like. After all getters and setters are just another public methods. You could use any other names.
But if you use frameworks like Spring, you are bound to use those standard names and you should never put your custom codes inside them.
absolutely yes, it's a bad pratice.
Imagine you communicate accross network with a third party (remoting, COM, ...), this will increase the round-trip and then hit application performance.
A setter could modify as part of validation, but a getter should return the value and let the validation be done by the caller. If you do validate, then how should be documented.
This actually highly depends on the contract you want to enforce with your get()-method. According to design-by-contract conventions the caller has to make sure that the preconditions are met (which means doing a validation in a setter method often is actually bad design) and the callee (I do not know if that's the correct english term for that, i.e., the called one) makes sure that the post conditions are met.
If you define your contract so that the get()-method is not allowed to change the object then you are breaking your own contract. Think about implementing a method like
public isValid() {
return (this.myvalue == null || this.myvalue.isEmpty());
}
Advantage of this approach is that you do not have to check wether the return of your get() is "N/A" or something else. This also can be called before calling set() to validate that you do not insert illegal values into your object.
If you want to set a default value you should do that during initialization.
State changes in getters should be a hanging offence. It means that client code must be careful about the order in which it accesses getters and setters and to do this it must have knowledge of the implementation. You should be able to call the getters in any order and still get the same results. A related problem occurs when the setter modifies the incoming value depending on the current state of the object.
You can use some value holder for this purpose. Like Optional class in guava library.