Reputation: 30879
In Dart, what's the difference between List.from
and List.of
, and between Map.from
and Map.of
? Their documentation is not totally clear:
/**
* Creates a [LinkedHashMap] instance that contains all key/value pairs of
* [other].
*
* The keys must all be instances of [K] and the values of [V].
* The [other] map itself can have any type.
*
* A `LinkedHashMap` requires the keys to implement compatible
* `operator==` and `hashCode`, and it allows `null` as a key.
* It iterates in key insertion order.
*/
factory Map.from(Map other) = LinkedHashMap<K, V>.from;
/**
* Creates a [LinkedHashMap] with the same keys and values as [other].
*
* A `LinkedHashMap` requires the keys to implement compatible
* `operator==` and `hashCode`, and it allows `null` as a key.
* It iterates in key insertion order.
*/
factory Map.of(Map<K, V> other) = LinkedHashMap<K, V>.of;
/**
* Creates a list containing all [elements].
*
* The [Iterator] of [elements] provides the order of the elements.
*
* All the [elements] should be instances of [E].
* The `elements` iterable itself may have any element type, so this
* constructor can be used to down-cast a `List`, for example as:
* ```dart
* List<SuperType> superList = ...;
* List<SubType> subList =
* new List<SubType>.from(superList.whereType<SubType>());
* ```
*
* This constructor creates a growable list when [growable] is true;
* otherwise, it returns a fixed-length list.
*/
external factory List.from(Iterable elements, {bool growable: true});
/**
* Creates a list from [elements].
*
* The [Iterator] of [elements] provides the order of the elements.
*
* This constructor creates a growable list when [growable] is true;
* otherwise, it returns a fixed-length list.
*/
factory List.of(Iterable<E> elements, {bool growable: true}) =>
new List<E>.from(elements, growable: growable);
Is the difference related to generics? Maybe the .from
factories let you change the type of the list, while the .of
ones do not? I come from a Java background, which works with type erasure, and maybe types are reified in Dart and you cannot use casts or raw types to change list/map types?
Upvotes: 40
Views: 13767
Reputation: 267614
List.of()
and toList()
They are used to create a new list of the same type as the original, but List.of()
can be used to upcast:
var ints = <int> [0];
var newList1 = ints.toList(); // List<int>
var newList2 = List<num>.of(ints); // List<num>
You can also copy a list by doing:
var newList3 = [...ints]; // List<int>
var newList4 = [for (var v in ints) v]; // List<int>
List.from()
Use this if you want to downcast and therefore it is important that the subtype is a type of supertype.
var ints = List<int>.from(<num>[0, 1]); // Good as all elements are of type `int`
var ints = List<int>.from(<num>[0, 1.5]); // Bad as some elements are of type `double`
Upvotes: 5
Reputation: 511846
Whenever possible it is better to use collection literals now rather than the .from
or .of
constructors. Apparently there are some performance benefits to this. (See link at bottom.)
Examples:
something.toList()
[...something]
Exception:
.from
if you need to downcast.If you do use them, though, you should always include the type.
Source: Dart team engineer's post
Upvotes: 4
Reputation: 21441
The important difference between the from
and of
methods are that the latter have type annotations and the former do not. Since Dart generics are reified and Dart 2 is strongly typed, this is key to both ensuring the List/Map
is correctly constructed:
List<String> foo = new List.from(<int>[1, 2, 3]); // okay until runtime.
List<String> bar = new List.of(<int>[1, 2, 3]); // analysis error
And ensuring that the types are inferred correctly:
var foo = new List.from(<int>[1, 2, 3]); // List<dynamic>
var bar = new List.of(<int>[1, 2, 3]); // List<int>
In Dart 1 types were completely optional, so many APIs were untyped or partially typed. List.from
and Map.from
are good examples, since the Iterable/Map
passed into them doesn't have a type parameter. Sometimes Dart can figure out what the type of this object should be, but sometimes it just ended up as List<dynamic>
or Map<dynamic, dynamic>
.
In Dart 2 the type dynamic
was changed from being both a top (Object) and bottom (null) type to only being a top type. Thus if you created a List<dynamic>
accidentally in Dart 1 you could still pass it to a method which required a List<String>
. But in Dart 2 List<dynamic>
is almost the same as List<Object>
, so this would fail.
If you are using Dart 2, you should always use the typed version of these APIs. Why do the old ones still exist, and what are the plans there? I don't really know. I would guess they would be phased out over time, along with the rest of the Dart 1.
Upvotes: 50