A common thing I do in my web application is request a resource from the server and handle it as a Map in dart.
import 'dart:convert';
const String sampleJSON = '''
{
"member": {
"meaningOfLife": 42
}
}
''';
Map<String, dynamic> getResource() {
// do some magic
return JSON.decode(sampleJSON);
}
I live with the assumption that all keys in a JSON decoded Map will be Strings but obviously I have no clue of the value's type. In checked mode this worked fairly well.
Analysis in strong mode to the code above will tell me: Unsound implicit cast from dynamic to Map<String, dynamic>
Question:
What is a good strategy to handle such cast warnings?
Questionable options 1:
Map getResource() {
// do some magic
return JSON.decode(sampleJSON);
}
Later this could be a problem: Iterable<String> keys = getResource().keys will give a warning.
Questionable option 2:
Map<String, dynamic> getResource() {
// do some magic
return new Map<String, dynamic>.from(JSON.decode(sampleJSON));
}
Does it not degrade performance much? And I will still get a warning for Map<String, dynamic> meaning = getResource()["member"];
Thanks for the advice.
If you know the type then just make it explicit
return JSON.decode(sampleJSON) as Map<String,dynamic>;
The Flutter team forbids the use of as because it introduces a runtime check and has performance implications. If it is relevant, depends on your requirements.
Alternatively, you can just silence the warning (might not yet work in your Dart version)
// ignore: STRONG_MODE_DOWN_CAST_COMPOSITE
return JSON.decode(sampleJSON);
See also Suppress hint about use of protected member
Related
In Java, we can serial any object like this:
Object anyObj = new Object();
String json = JSON.tostring(anyObj);
but in Dart, we must define toJson method like this in every single Object:
import 'dart:convert';
String toJson() => json.encode(toMap());
and define toMap:
Map<String, dynamic> toMap() => {}
any simple way? Image if the system has 10000+ objects, are we write toJson function for every object? Is it possible to serial object to json like Java way in Dart?
Most Java libraries that provide serialization of arbitrary objects rely on runtime reflection to know what fields are present on an object (and their types, etc).
Runtime reflection is technically possible in Dart (using dart:mirrors) but the library is unstable and is not available on all platforms (notably runtime reflection is disabled in Flutter).
The idiomatic Dart way to do this is with code generation. In general, you add the required annotations/etc, then run flutter pub run build_runner build to use the build_runner package to generate extra code.
A couple of popular libraries are:
json_serializable is useful if you have a model class that you want to add toJson() and fromJson() methods to:
#JsonSerializable // marks the class for json code generation
class Dog {
final String name;
final int age;
Dog(this.name, this.age);
// boilerplate for generated implementations
Map<String, dynamic> toJson() => _$DogToJson(this);
factory Dog.fromJson(Map<String, dynamic> json) => _$DogFromJson(json);
}
built_value is a more all-encompassing approach. The main purpose of the library is to provide deep immutability for model classes, similar to Kotlin's data class. However, it also provides good serialization support out of the box. There is a fair amount of boilerplate, but it can definitely be worth it (especially if you use the vscode plugin to write it for you):
abstract class Dog extends Built<Dog, DogBuilder> {
Dog._();
factory Dog([void Function(DogBuilder) updates]) = _$Dog;
String get name;
int get age;
}
You can then use this code like:
final dog = Dog((b) {
b.name = 'name';
b.age = 123;
});
final helloDog = dog.rebuild((b) => b.name = 'hello');
This blog post goes through serialization in detail: https://medium.com/dartlang/darts-built-value-for-serialization-f5db9d0f4159#.h12y94wu7
Both libraries are maintained by Google
class Product {
final int id;
Product({this.id});
factory Product.fromMap(Map<String, dynamic> map) {
return Product(
id: map['id'] as int, // Why as?
);
}
}
This is a pattern I have seen used by Google, the main question is what's the need of using as there, because below code does the job equally well.
Product(
id: map['id'],
)
Can anyone tell me any advantage of using as in above code?
This comes down to how the analyzer is configured and if you have disabled implicit-casts in analysis_options.yaml:
analyzer:
strong-mode:
implicit-casts: false
The problem in your code is that the type of the map is defined as Map<String, dynamic> so we don't really know the type of the values in the map on compile time. And if we have disabled implicit casting we cannot just assign a dynamic into a int variable without any implicit type casting with as.
It is recommended to use implicit-casts: false (and implicit-dynamic: false) to make the analyzer more strict about your typing which can both make more readable code but only catch errors where you are casting types to other types without your knowing which in the end could have give a runtime error.
There are more about in the documentation:
https://dart.dev/guides/language/analysis-options
Coming back to C++ after a hiatus in Java. Attempting to create an immutable object and after working in Java, a public const variable seems the most sensible (like Java final).
public:
const int A;
All well and good, but if I want to defensive check this value, how might I go about it. The code below seems strange to me, but unlike Java final members, I can't seem to set A in the constructor after defensive checks (compiler error).
MyObj::MyObj(int a) : A(a) {
if (a < 0)
throw invalid_argument("must be positive");
}
A public const variable for A seems like a clearer, cleaner solution than a getter only with a non const int behind it, but open to that or other ideas if this is bad practice.
Your example as it stands should work fine:
class MyObj {
public:
const int var;
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
};
(Live example, or with out-of-line constructor)
If you intend that MyObj will always be immutable, then a const member is
probably fine. If you want the variable to be immutable in general, but still have the possibility to overwrite the entire object with an assignment, then better to have a private variable with a getter:
class MyObj {
int var;
public:
MyObj(int var) : var(var) {
if (var < 0)
throw std::invalid_argument("must be positive");
}
int getVar() const { return var; }
};
// now allows
MyObj a(5);
MyObj b(10);
a = b;
Edit
Apparently, what you want to do is something like
MyObj(int var) {
if (var < 0)
throw std::invalid_argument("must be positive");
this->var = var;
}
This is not possible; once a const variable has a value it cannot be changed. Once the body ({} bit) of the constructor starts, const variables already have a value, though in this case the value is "undefined" since you're not setting it (and the compiler is throwing an error because of it).
Moreover, there's actually no point to this. There is no efficiency difference in setting the variable after the checks or before them, and it's not like any external observers will be able to see the difference regardless since the throw statement will unroll the stack, deconstructing the object straight away.
Generally the answer by N. Shead is the regular practice - but you can also consider:
Create domain-specific types and use them instead of general primitives. E.g., if your field is a telephone number, have a type TelephoneNumber which, in its constructor (or factory), taking a string, does all the telephone number validation you'd like (and throws on invalid). Then you write something like:
class Contact {
const TelephoneNumber phone_;
public:
Contact(string phone) : phone_(phone) { ... }
...
When you do this the constructor for TelephoneNumber taking a string argument will be called when initializing the field phone_ and the validation will happen.
Using domain-specific types this way is discussed on the web under the name "primitive obsession" as a "code smell".
(The problem with this approach IMO is that you pretty much have to use it everywhere, and from the start of your project, otherwise you start having to have explicit (or implicit) casting all over the place and your code looks like crap and you can never be sure if the value you have has been validated or not. If you're working with an existing codebase it is nearly impossible to retrofit it completely though you might just start using it for particularly important/ubiquitous types.)
Create validation methods that take and return some value, and which perform the validation necessary - throwing when invalid otherwise returning its argument. Here's an example validator:
string ValidatePhoneNumber(string v) {
<some kind of validation throwing on invalid...>
return v;
}
And use it as follows:
class Contact {
const string phone_;
public:
Contact(string phone) : phone_(ValidatePhoneNumber(phone)) { ... }
I've seen this used when an application or library is doing so much validation of domain-specific types that a small library of these domain-specific validator methods has been built up and code readers are used to them. I wouldn't really consider it idiomatic, but it does have the advantage that the validation is right out there in the open where you can see it.
Dart offers a FutureOr class, that allows writing:
FutureOr<int> future;
future = 42; // valid
future = Future.value(42); // also valid
I would assume that FutureOr would be useful to remove the unnecessary delay caused by the event loop if the value can be read synchronously.
But that doesn't seem to be the case, as showcased by:
import 'dart:async';
void main() async {
print('START');
futureOrExample();
print('END');
}
void futureOrExample() async {
FutureOr<int> futureOr = 42;
print('before await');
await futureOr;
print('end await');
}
which prints:
START
before await
END
end await
when I would expect:
START
before await
end await
END
In that case, why does FutureOr (or more generally await 42) work this way?
Similarly, what's the purpose of FutureOr in that situation since it produces the same result as Future?
I know that I could use SynchronousFuture to achieve the desired result, but I'm just trying to understand what's the use of FutureOr.
The use of FutureOr, as introduced with Dart 2, is to allow you to provide either a value or a future at a point where the existing Dart 1 API allowed the same thing for convenience, only in a way that can be statically typed.
The canonical example is Future.then. The signature on Future<T> is Future<R> then<R>(FutureOr<R> action(T value), {Function onError}).
The idea is that you can have an action on the future's value which is either synchronous or asynchronous. Originally there was a then function which took a synchronous callback and a chain function which took an asynchronous callback, but that was highly annoying to work with, and in good Dart 1 style, the API was reduced to one then method which took a function returning dynamic, and then it checked whether it was a future or not.
In Dart 1 it was easy to allow you to return either a value or a future. Dart 2 was not as lenient, so the FutureOr type was introduced to allow the existing API to keep working. If we had written the API from scratch, we'd probably have done something else, but migrating the existing asynchronous code base to something completely different was not an option, so the FutureOr type was introduced as a type-level hack.
The await operation was also originally defined to work on any object, long before FutureOr existed. For consistency and smaller code, an await e where e evaluated to a non-future would wrap that value in a future and await that. It means that there is only one quick and reusable check on a value (is it a future, if not wrap it), and then the remaining code is the same. There is only one code-path.
If the await worked synchronously on non-Future values, there would have to be a synchronous code path running through the await, as well as an asynchronous path waiting for a future. That would potentially double the code size, for example when compiling to JavaScript (or worse, if there were more awaits in the same control flow, you could get exponential blow-up for a naive implementation). Even if you avoided that by just calling the continuation function synchronously, it would likely be confusing to some readers that an await would not introduce an asynchronous gap. A mistake around that can cause race conditions or things happening in the wrong order.
So, the original design, predating FutureOr, was to make all await operations actually wait.
The introduction of FutureOr did not change this reasoning, and even if it did, it would now be a breaking change to not wait in places where people expect their code to actually give time for other microtasks to run.
The await keyword always lock the function execution.
Writing:
await 42
Is equivalent to:
await Future.value(42)
The reason being:
This is how await works in Javascript
it makes the behavior of await consistent.
Now, what's the purpose of FutureOr then?
FutureOr was never intended as a way to potentially make await synchronous.
Instead, it is an implementation detail of Future.
Without FutureOr, writing the following would not compile:
Future(() {
return 42; // compile error, not a Future
});
Future<int> future;
future.then((value) {
return value * 2; // compile error, not a Future
});
Instead, we would have to wrap all values in a Future.value like so:
Future<int> future;
future.then((value) {
return Future.value(value * 2);
});
for those who are still confused
I found good explanation https://itnext.io/what-is-futureor-in-dart-flutter-681091162c57 without diving into details
This piece of code could explain the target and real use cases of FutureOr
abstract class IDBService {
FutureOr<String> fetch();
}
class FirebaseRemoteService extends IDBService {
#override
Future<String> fetch() async => await 'data';
}
class LocalHiveDbService extends IDBService {
#override
String fetch() => 'data';
}
so in implementations of IDBService
the return type can be Future or String at the same time now!
Coming late to the discussion.
Updating my Dart comprehension - pardon my C++/JS -ish approach.
Seems like this would be useful for singleton initiation. Consider following:
import 'dart:async';
class AClass {
static String _info = '';
static FutureOr<String> get info async {
if (_info.isEmpty) {
print('--> is empty...');
_info = await Future.delayed(Duration(seconds:2),
() => "I'm alive!!");
}
else {
print('--> not empty');
}
return _info;
}
}
Future<void> main() async {
String info = await AClass.info;
print('Fist call: ' + info);
info = await AClass.info;
print('Second call: ' + info);
}
It works as expected - in either case, whether the _info member has been instantiated or not, the getter returns a valid string.
It works fine if I just use a Future<String> specifier in the getter, too. The current implementation makes FutureOr seem mostly like a self-documentation exercise (can return a Future<String> or a String...)
But, even if await currently always locks the execution, a future update may allow it to work as expected, in which case using the FutureOr construct would anticipate updates.
(Aside: I imagine this example could be condensed using an Optional wrapping the _info member, but that's a different exercise...)
I needed to use FutureOr today. I wanted to call a function that might be asynchronously (not always).
String callbackOne() => "hello";
Future<String> callbackTwo() async => (await Future.delayed(Duration(seconds: 1),() => "This is a sentence"));
Problem
I can do getLengthOfResult(callbackOne), but not getLengthOfResult(callbackTwo). Conversely, if accept an async callback, I can't use the sync callback.
DartPad
Future<int> getLengthOfResult(String Function() callback) async {
return callback().length;
}
Solution
DartPad
Future<int> getLengthOfResult(FutureOr<String> Function() callback) async {
// I can await on callbackOne, even though it returns a String.
final result = await callback();
return result.length;
}
main() {
getLengthOfResult(callbackOne);
getLengthOfResult(callbackTwo);
}
I previously had the following code, it works fine. (note that Card, SearchResults, Quiz all extend Persistable, and Persistable contains the constructor .fromMap.
Persistable fromString(String value){
Map<String, dynamic> m = parse(value);
switch(m['type']){
case 'card':
return new Card.fromMap(m);
case 'searchresults':
return new SearchResults.fromMap(m);
case 'quiz':
return new Quiz.fromMap(m);
}
}
It was a bit wordy, so I thought I would break it down into two parts. I first have this:
static final Map<String, Persistable> lookup =
{'card': Card,
'searchresults': SearchResults,
'quiz': Quiz };
Seems reasonable, but then when I try to redefine the method, I get confused.
Persistable fromString(String value){
Map<String, dynamic> m = parse(value);
String type = m['type'];
Persistable p = lookup[type];
... Confused, this can't be right
... ultimately want to "return new p.fromMap(m)";
}
Persistable p really means a instance of class Persistable. How do I type my lookup map so that its values are of the class Persistable, so that I can call their .fromMap constructors?
First of all I think your initial approach is perfectly valid and should not be cast away owing simply to its verbosity.
I believe alternative approaches introduce additional complexity and are justified only if you are really in need of dynamic dispatch. (For example if you write library for persistency and you wish to add ability to register arbitrary class for persistency for clients of library)
If dynamic dispatch is a must for you I believe there is two main possibility:
- Reflection API. Recently reflection library got sync API, so this way is now much more affordable then before. I believe there always will be some cost incurred by reflection anyway.
- Use core DART functionality.
With the second approach you may use some sort of trick to imitate constructor call dynamically.
For instance you may store in map not Type variable but function which returns instance of required class:
So your code may look something like
static final Map<String, Function> lookup = new Map<String, Function>
static void registerClass(String className, factory) {
lookup[className] = factory;
}
static Persistable getInstance(String className, Map map){
return lookup[className](map);
}
And on client side:
....
registerClass('quiz', (map)=> new Quiz.fromMap(map));
registerClass('card', (map)=> new Card.fromMap(map));
(Attention - I did not test this)
You may look for working sample code for that approach in https://github.com/vadimtsushko/objectory