Difference between Object, Dynamic and Var in Dart? - 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).

Related

Dart: dynamic vs nullable Object

Is there any difference between dynamic and Object? in dart?
This question may sound a duplicate of this What is the difference between dynamic and Object in dart?. However, I think it is not. I am not talking about semantic difference or any warnings thrown by the dart analyzer, or anything from a coder's perspective. I want to know is there a real difference between both under the hood.
I can assign any data value to both.
When I run the following:
Object? a;
dynamic b;
print(a.runtimeType);
print(b.runtimeType);
I get:
Null
Null
I know dynamic is a keyword, while Object? is a Object class. But does dynamic infer to Object?.
I'm new to Dart lang. So, please enlighten me.
Yes, there is a difference.
The types dynamic and Object? are equivalent wrt. subtyping. Both are "top types" which means that every type is a subtype of them both, even each other.
So, for subtyping there is no difference.
The difference is entirely in what you can do with an expression that has one of those types.
If an expression has type Object?, then the only methods you can call on it are the methods of Object and Null. The only types you can assign the expression to are top types.
If the expression has type dynamic, it is as if the static type system has been turned off.
You are allowed to call any method (like dynamicExpression.arglebargle()) without any warning. If the method isn't there at runtime, it'll throw an error.
And you can assign the value to any type. If the value turns out to not have that type at runtime, it'll throw an error. (This is usually called "implicit downcast" because it works as if an is ExpectedType was added to the expression by the compiler.)
Also, because a dynamic expression is treated as having any method, you cannot call extension methods on it.
It's like dynamic is a type alias for Object? with the extra effect of turning off static type checking.
When you declare a variable as an Object?, during compile-time the compiler knows that type of the variable is Object? and it remains Object? forever. You can assign any type to this variable because every other type either extends the Object or null.
When you declare a variable as a dynamic, during compile-time the compiler does not know the type of the variable and just ignores it. The compiler will check the type only during run-time and will infer the type according to the value you assigned.
dynamic contains Exception. Object can only represent known data types and Null, (excluding Exception)

Is "dynamic" a data type in Dart?

I have read that dynamic is a data type in Dart, but is it correct to call it a data type? it doesn't seems like a data type, it seems like it is a way to allow your variable to be of any data type.
In Dart, dynamic is a type. It belongs in the type hierarchy and is related to other types by the subtype relation.
It's a "top" type, which means that every type is a subtype of dynamic. (Including itself, because the "subtype" relation is reflexive - every type is considered a subtype of itself, and the term "proper subtype" is used when only talking about subtypes that are not also supertypes.)
Being a top type, it means any value can be assigned to a variable of type dynamic. So can they to any other top type, which mainly means Object?.
The difference between the two is that:
An expression with static type dynamic can be assigned to any type. That's obviously unsafe, so the runtime inserts a check, a so called "implicit downcast" which works just like doing as TargetType.
You can call any member on an expression with static type dynamic. That's obviously unsafe, so the runtime will throw if the object doesn't have such a member.
That kind of runtime checked unsafe behavior (not static type-checked) is the reason the type is named dynamic. Using dynamic is a way to turn off the static type system. Use with extreme care.
Whether you can call dynamic a "data type" depends on what you mean by "data type". The Dart language specification doesn't use the term "data type" about anything.
Yes. The specifications call it a type (https://www.ecma-international.org/publications-and-standards/standards/ecma-408/)
The type dynamic denotes the unknown type. If no static type
annotation has been provided the type system assumes the declaration
has the unknown type
Type dynamic has methods for every possible identifier and arity, with
every possible combination of named parameters. These methods all have
dynamic as their return type, and their formal parameters all have
type dynamic. Type dynamic has properties for every possible
identifier. These properties all have type dynamic

Why is asigning a dynamic to a non-nullable not an error with sound null safety

In dart with sound null safety turned on it is entirely possible to do
dynamic myVar; // myVar assumes default value of null
String someString = myVar; // No warning by IDE.
As expected the above results in a run-time error since myVar is null and someString is non-nullable.
Is this a problem with the linter/IDE?
I did discover that I can enable pedantic linting that causes the IDE to show a warning when I try to implicitly cast dynamic to another type. Turning that on helps but I think the problem is that dynamic can be null without having to be explicitly defined as nullable.
In short we don't have
dynamic? myNullableVar;
My questions is: Is this a bug?
This "issue" bites you most commonly when you do something like
Map<String, dynamic> myData = jsonDecode(receivedResponseBody);
var name = myData['name'];
In the above example I expect the IDE to show a warning that I am trying to assign a potentially null value to a non-nullable variable. The IDE should require the user to add a null check.
If this is not a bug (in the IDE or in the linter), then why not?
Long story short: The implicit cast from dynamic to another type masks the issue with null assignments, and I expect the IDE to provide a warning.
EDIT: after note from # jamesdlin below
Specifically I am OK with the following where the Left-hand side of the assignment allows null values.
dynamic someVar;
String? myString = someVar;
Side note, I was hoping that the new dart typeDef feature would allow my to build something similar to Union type hints in Python. That would allow me to then get something like
typeDev JsonValueType = { int, String, float, bool };
And the result from jsonDecode would then be
Map<String, JsonValueType?>
Which explicitly includes Null and therefore the IDE would warn the user to add a null check.
My opinion: As long as you can assign any nullable type on the right of an assignment operator to a non-nullable type on the left, you don't have true sound null safety.
It's not a bug.
You can do:
dynamic x = "not an int";
int y = x;
and not get any compiler warnings. That's just how dynamic works, it turns off compiler ("static") type-checks and relies on run-time ("dynamic") checks instead. That's where the name comes from.
There is no reason to allow dynamic? because dynamic already allows the null value (and any other value). The dynamic type is inherently nullable, adding ? to it makes no difference, just as Null? is meaningless.
The Dart type system is statically null sound (mostly, the usual caveats around generics apply), but dynamic is a way to turn that type system off on request. Don't use dynamic unless you want that effect.

Dart generics not reified as per the docs

I'm trying to pass a type in order to make use of the type information, but that types doesn't appear to be pass through.
I went back to the docs to double check that Dart generics are in fact reified and according to the docs, they are:
I call hydrate on a response which morphs the content of response object:
response.hydrate<BoqVO>();
I'm expecting T to be of type BoqVO:
class Response {
...
void hydrate<T>() {
print(T.runtimeType); // always prints _Type
if (T is BoqVO) {
print("IF");
} else {
print("ELSE"); // always goes into ELSE block
}
}
...
}
... but it's not.
Replacing response.hydrate<BoqVO>(); with response.hydrate(new BoqVO()); and changing the method signature to
void hydrate(T t) {
works if i now use lowercase t, but one shouldn't have to instantiate the object in order for reified generics to be available.
Any ideas why Dart is doing this or what i'm missing for reified generics to work correctly?
PS: I'm not on Dart 2 yet, currently on Dart 1.24.3
As Günther Zöchbauer has said, the type parameter doesn't work in Dart 1.24.
The following explains what would happen if you tried the same code in Dart 2.0, where it would also not work, because it uses the type parameter incorrectly.
The code T.runtimeType treats T as an expression. When a type, including a type parameter, is used as an expression, it evaluates to an instance of the class Type. What you print is the runtime type of that Type object (where _Type is an internal platform implementation of Type).
To print the real type, just print(T) (that still converts T to a Type object, but one representing the type BoqVO and with a toString that includes the BoqVO name).
Likewise for T is BoqVO, you evaluate T to a Type object, and since Type doesn't implement BoqVO, that test is always false. There is no simple way to test if the type of a type parameter implements a specific other type, but you can hack around it as <T>[] is List<BoqVO>.
Generic collections were supported from the beginning and they got some type support, but generic methods were only experimental in Dart 1 and reified type parameters were only added in Dart 2 pre releases.

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