Dart: Explain the "get" here [duplicate] - dart

I'm learning Dart & Flutter, but I'm struggling with some basic programming issues like the use of getters:
GoogleSignInAccount get user => _user!;
What's the equivalent of the "get" method?
What does the ! at the end of a variable mean?
Thanks in advance!

That is a getter, in Java that code might look like:
public GoogleSignInAccount getGoogleUser(){ return this.user; }
Dart likes that code written more succinctly.
In Dart private class members are denoted via a _ in front of the variable/function name. So the variable that the getter is returning is a private variable, hence _user.
The ! at the end of the variable name has to do with Dart null safety. Throwing a ! at the end of the variable name is functionally the equivalent of saying assert _user != null; what it actually does is cast a nullable data type to its non-nullable equivalent.
In Dart, all data has two types, a nullable type, denoted by a ? at the end of the data type declaration, and a non nullable type.
The _user variable, it can be assumed from this code, is of the type GoogleSignInAccount? meaning that it can be null. However the getter wants to return a GoogleSignInAccount. Because there is no question mark on the end, the type the getter returns must NOT be null. So we put ! on the end of the _user variable to denote that we want to cast the nullable type to its not null form.
Note also that the name of this function is user and that in Dart you can have two functions of the same name if one is a getter and the other is a setter. To denote getter versus setter in a function declaration, you use the get and set keywords as you see above.
If you want to get a good idea of how getters and setters look in Dart, make a class with some variables. Make sure all of the variable names start with _ so that they are private, then right click in your IDE and tell it to generate some getters and setters for you. I believe both Android Studio and VSCode have the Generate option in their right click menu.

There are two things at play here:
1.
GoogleSignInAccount get user => _user!;
does the same as
GoogleSignInAccount user() {
return _user;
}
Where the first one is called a getter.
As a getter you can access it like a property. For example
myUser = userService.user;
With the function notation you access it like
myUser = userService.user();
Since you do not calculate anything but only expose that private member the getter notation is more succinct.
2.
Regarding the !:
Dart is null safe, which means types without a ? can't be null.
In your case the _user is a GoogleSignInAccount? which means it can be null. For example on startup when the user isn't signed in yet.
The getter GoogleSignInAccount get user has the type GoogleSignInAccount which means the compiler gives you an error when you try to assign null to it.
So
user can not be null.
_user could be null.
With the ! you promise to the compiler that you know that the _user is in no case null when the getter user is called.
Example:
This could be the case if the user is loaded right on startup while a progress indicator is shown. When the user is loaded you start the whole app and now you can access the user with user!. You are sure that the user is loaded.
If you somehow access the user before it's loaded (while it's still null) you get a runtime error.
Null safety just helps you to think about wheter a variable can be null and to avoid NullPointerExceptions.

The exclamation mark at the end of the private variable tells the compiler that the variable is not null and the user data returned by the getter must not be null.

Related

Difference between Object, Dynamic and Var in Dart?

There is a discussion about dynamic and var before null-safety. Then what's the Object? between each of them?
Is Object? == dynamic?
How about var? and dynamic??
Any difference between dynamic? and dynamic?
I see the official document about null-safety, but can't find the related topic.
dynamic is a special type that disables static type-checking. You can attempt to call any method on a dynamic type. If the object turns out not to have such a method, then it will result in a runtime failure instead of a compile-time one.
Object? is a base type suitable for referencing any object, including null. Unlike dynamic, it is statically type-checked, so you would get compile-time failures if you attempt to call most methods on it without explicitly checking the runtime type or without performing a cast.
var? is not valid syntax. var is not a type; it declares a variable without explicitly specifying a type, allowing the type to be inferred.
dynamic? is valid but is redundant. (See #3.)
Variables of type dynamic can already include null, so adding a ? to make it nullable is redundant. The Dart analyzer will tell you so.
3: About dynamic vs dynamic?: they are the same.
Since dynamic also represents nullable types, for the compiler it is the same of dynamic?.
From Why operator ==(Object other) accepts a nullable argument?:
You can also see the analyzer and runtime will call it dynamic even if we check the signature of a method declared to return dynamic?:
void main() {
print(test.runtimeType); //runtimeType of the test function: () => dynamic
}
dynamic? test() { }
In fact a hint of the dart linter reports as 'unnecessary' the use of ? in dynamic? (as in Null?):
The '?' is unnecessary because 'dynamic' is nullable without it. (unnecessary_question_mark).
Personally, I don't understand why dynamic? Is only reported by a hint (which many people, myself included, don't notice) keeping it valid as a syntax.
1: A variable declared with Object? type behaves like all other normal variables whose type is specified, such as String? etc. Since every class -apart Null (the type of null)- is a subclass of Object (and since in Dart there are no primitive values as opposed to objects, unlike in Java; in Dart also null, int and bool are objects. But forget this clarification, if you don't know Java), a variable declared with Object? can contain any value. But the compiler will only allow access -after a null check- to the properties of Object (toString(), ==(), runtimeType, etc).
A variable declared with dynamic (or dynamic?, see point 3) instead allows access to any public member: the compiler will not perform any checks (unless the property begins with an underscore _, because in that case it is clear that it is not public); if you try to access a non-existent member you will instead have an error at runtime Note1. Furthermore, with dynamic we also renounce null safety: dynamic is equivalent to dynamic? (in practice the question mark can be considered implicit, it is as if it were always there).
2: using var, or final -if you want an immutable reference- without declare the type, the compiler check the value assigned to the variable (in fact the omission of the type is not allowed if the variable is not initialized immediately) and treats the variable as if it were declared with that type.
dynamic at runtime:
One use of 'dynamic' that can lead to confusion is with generic classes, because dynamic as parametric type exists also at runtime:
with
dynamic obj = true;
obj at runtime has bool type, but with
List list = [bool];
list at runtime has List<dynamic> type.
However, using
var list2 = [true];
the parametric type is inferred correctly (list2 has List<bool> runtimeType).
Note1 More precisely, a invocation such as myDynamicVariable.nonexistentMember cause an invocation of the noSuchMethod() method on the object; noSuchMethod() can also be overridden and not throw any exception; but this is a rare practice, in Dart 2).

What is the reason behind not able to use this. keyword and datatypes like string and int in named constructor in Dart

Why in named constructor not able to use datatypes and this keyword? It shows Error:
The parameter 'name' can't have a value of 'null' because of its type, but the implicit default value is 'null'.
Try adding either an explicit non-'null' default value or the 'required' modifier.dart(missing_default_value_for_parameter)
Your problem is that, if nothing else is specified, the default value for optional named parameters is null. So what your code actually does right now is setting the variable late String name to null which is not allowed because of the type String. If you want to allow null, the type should be String?.
The late keyword is used in situations where you cannot set the variable when initializing the object and is just a promise to the Dart compiler that the variable will have a value when you are going to use the variable at some point. (the compiler will add a check to make sure the variable is actually given a value before use).
But late is not a way to allow null as a valid value.
Another problem in your code is that it does not really make sense to do:
person({this.name}) {
this.name = name;
}
The reason is that {this.name} is already used for setting the variable name. When you later do: this.name = name then both name is pointing to the same variable in your object.
Based on the rest of your code I think you should remove the late keyword and instead use required to make sure the user of your class are specifying your named parameter. So something like this should work (also, please use Person instead of person for naming classes):
class Person {
String name;
int age;
Person({required this.name, required this.age});
void sayName() => print(name);
}
void main() {
final p = Person(name: 'Mathew', age: 24);
p.sayName();
print(p.age);
}
I removed the named constructor Person.age since I don't think it makes much sense to make a Person without a name. If name and age is truly optional, you can instead change the types to String? and int? and remove the required keyword.
This will make it clear when using your class that these two variable can be empty and the user of the class should remember to check for possible null values.
I recommend reading more about Dart null safety feature starting here: https://dart.dev/null-safety

Error: The instance member ... can't be accessed in an initializer

Why does this code:
class _SequentialTextPageState {
String jsonTextPref = 'seqtext';
int jsonTextSuff = 10;
String jsonText = jsonTextPref + jsonTextSuff.toString();
}
generate these errors?
Error: The instance member 'jsonTextPref' can't be accessed in an initializer.
Error: The instance member 'jsonTextSuff' can't be accessed in an initializer.
It seems to me that concatenation between String and int is correct?
Dart initializes objects in multiple phases. Initializing members directly ("field initializers") occurs early in object initialization, before this becomes valid, so that phase cannot initialize members that depend on other parts of the object.
Dart provides multiple ways to initialize members, so if one member needs to depend on another, you can initialize it in a later phase by using a different mechanism. For example, you could do one of:
Add the late keyword to make the dependent member lazily initialized.
Move initialization of the dependent member into the constructor body.
In the case of a Flutter State subtype, you could initialize the dependent member in its initState method, which in some cases is more appropriate.
Note that in some cases you additionally can consider replacing the member variable with a read-only getter instead. For example, in your case, perhaps you could use:
String get jsonText => jsonTextPref + jsonTextSuff.toString();
That would be appropriate if jsonText should always depend on jsonTextPref and jsonTextSuff, would never need to have an independent value, and if it's acceptable for jsonText to return a new object every time it's accessed.
Dart does not allow field initializers to refer to the object itself. Fields must always be fully initialized before any access is given to the object begin created.
The initializers can only access static and top-level variables, not any instance variables on the object itself.
With null safety, you will be allowed to write late String jsonText = this.something + this.other;. That field will then not be initialized until it's first read or written, which is necessarily after the object itself has been created.
You can only use constant expressions as initializers. x=this.y is not constant.
The error was displayed when I did following:
class MyClass {
String id;
String[] imagePaths;
}
It will mark the String in the line String id; as error, but the error is in the next line, it should be List<String> imagePaths; instead of String[] imagePaths; then the error in the line above also disappears. This can be very confusing if you have a big class and the actual error is many lines underneath the first marked line (talking from experience...)

Why does an unitialized typed variable in Dart not implement the type's interface?

I started learning Dart and was reading a critique of some of it's design choices here: https://medium.com/#krossovochkin/dart-language-bad-design-choices-6e35987dc693
The last point that is made is about the poor type system and the author cited this code snippet which prints null:
void main() {
String s = null;
if (s is String) {
print("string");
} else if (s is Null) {
print("null");
} else {
print ("none");
}
}
The is keyword was new to me but "The Dart Programming Language" by Gilad pointed out that is checks the interface implemented by an object's class and not the actual class of an object.
However this didn't help me much because I would think that the variable s is an instance of String and therefore implements String, but the evidence is to the contrary.
I get that the class is not required when defining objects/variables in Dart, and thus I started to wonder if putting the class in the definition just serves as sugar and has little functional purpose. But instead the class of an object/variable is completely determined by its value, and since the default value for all variables in Dart is null, then it would make sense that String is not implemented, but Null is. Is this the case? Am I way of base? Maybe someone could help me wrap my head around this.
The reason is that is checks the interface of the current object itself and not the reference to this object. So yes, s can point to a String object but also allowed to point to null which are a instance of Null: https://api.dart.dev/stable/2.7.2/dart-core/Null-class.html
Since Null does not implement the String interface, this will return false (null is String). This is also mentioned in the article.
The problem the article are trying to focus on are more the fact you are allowed to set the String variable to null value but Null does not implement String.
Well, in the future, this problem are going to be fixed with non-nullable types which are in development right now. When this is implemented you can actually define variables where you can be sure the value will never be null.
So I continued my Dart reading and I came to a better understanding, and that is that Dart is truly optionally typed and that means 2 things:
Type are syntactically optional.
Type has no impact on runtime semantics.
Therefore the actual type annotation of a variable in Dart only serves documentation purposes and it cannot be assumed that a type annotation is true. The actual type of a variable is wholly determined by the value stored at this variable, and in this case it is null.
In truth the variable that I defined in my example is not a String variable or an implementer of the String interface. It is just annotated that it may be/should be/most likely is a string.

In Dart, do I annotate a function return value with dynamic or Object if I don't know it's type?

If I have a function that returns a value of an unknown type, do I use dynamic, representing any object, or Object, which is the ancestor of all other types?
The style guide discusses this question for parameters, but not for return values.
How should I annotate the return value and why?
Dart engineer Bob Nystrom writes:
Return types are an interesting twist on this problem. With parameter types, the guidelines are pretty straightforward:
If you use Object as a parameter type, you're saying "my method will safely accept any object and only use it for stuff like toString() that all objects support".
If you use dynamic (or nothing) as a parameter type, you're saying "Dart's type system can't easily express the type that I accept here" or "I didn't bother to annotate".
It's tricky to flip (1) around. For a return type, I guess Object would say "You better not call anything except toString() or other stuff all objects support before doing a type test yourself", where dynamic would I think mean "we can't easily annotate this so you and I better just know what we're doing".
The user would have to "cast" it to a specific type that they expect to see to avoid compiler warning and get an error earlier in checked mode.
For what it's worth, in many cases you wouldn't have to cast even if you return Object. Dart allows implicit downcasting when you initialize a local variable with a type annotation. So you can do:
Object foo() => 123;
main() {
int x = foo(); // Implicit downcast. No type warning.
}
I think in this case, I would probably do dynamic, though. I think that conveys "I don't know what type this returns, but you should" better than Object.

Resources