Dart литералы это ссылки?
Литералы в Dart это объекты т.к. мы можем сделать так: print(5.runtimeType); Получается что 5-это ссылка на ячейку памяти а не константа, хранящаяся прямо в коде?
Ответы (1 шт):
В Dart все является объектами, но фишка в реализации и оптимизации компилятора и Dart VM. Давайте все посмотрим на примерах.
var a = 5;
var b = true;
В Dart эти литералы действительно являются объектами, но компилятор и runtime оптимизируют их работу:
- Для частых значений может использоваться пул констант
- Примитивные типы могут оптимизироваться до машинных значений
- JIT/AOT-компиляторы могут выполнять дополнительные оптимизации
var str = "Hello";
Строки в Dart неизменяемы (immutable) и также могут оптимизироваться через пул констант.
var list = [1, 2, 3];
var map = {'a': 1, 'b': 2};
Эти литералы создают новые объекты в памяти.
Таким образом:
- Да, литералы в Dart - это объекты
- Но их реализация оптимизирована
- Малые целые числа и строки часто используют пул констант
- Компилятор может оптимизировать работу с примитивными типами
- Const литералы всегда являются одним и тем же объектом
void main() {
var a = 5;
var b = 5;
print(identical(a, b)); // true - одна и та же ссылка
b = a; // b теперь ссылается на тот же объект, что и a
print(identical(a, b)); // true - так как a и b ссылаются на один объект
b = 6; // b теперь ссылается на новый объект
print(identical(a, b)); // false - так как a и b ссылаются на разные объекты
// Компилятор может оптимизировать это
a = 5 + 1;
print(identical(a, b)); // true - так как a и b ссылаются на один объект
var x = 1000000;
const y = 1000000;
print(identical(x, y)); // true - одна и та же ссылка
var s1 = "hello";
var s2 = "hello";
print(identical(s1, s2)); // true - строки из пула
const c = 5;
const d = 5;
print(identical(c, d)); // true - одна и та же ссылка
var list1 = [1, 2, 3];
var list2 = [1, 2, 3];
print(identical(list1, list2)); // false - из-за мутабельности
const list3 = [1, 2, 3];
const list4 = [1, 2, 3];
print(identical(list3, list4)); // true - из-за иммутабельности (UnmodifiableListView)
var map1 = {'a': 1, 'b': 2};
var map2 = {'a': 1, 'b': 2};
print(identical(map1, map2)); // false - из-за мутабельности
const map3 = {'a': 1, 'b': 2};
const map4 = {'a': 1, 'b': 2};
print(identical(map3, map4)); // true - из-за иммутабельности (UnmodifiableMapView)
}
Также в данном вопросе уместно учитывать и передачу данных. В Dart передача данных зависит от типа данных:
- Для примитивных типов (как мы узнали они тоже объекты) - передача по значению
- Для объектов - передача по ссылке
class Person1 {
Person1(this.name);
String name;
}
class Person2 {
const Person2(this.name);
final String name;
}
void modifyValues(int number, List<int> list, String str, Person1 p1, Person2 p2) {
number = 10; // Не изменит оригинал
list.add(4); // Изменит оригинал
str = "world"; // Не изменит оригинал
p1.name = "Bob"; // Изменит оригинал
// p2.name = "Bob"; // Не даст изменить из-за final
}
void modifyValues2(
final int number, final List<int> list, final String str,
final Person1 p1, final Person2 p2) {
// number = 10; // Не даст изменить из-за final
list.add(8); // Изменит оригинал
// str = "world"; // Не даст изменить из-за final
p1.name = "Bib"; // Изменит оригинал
// p2.name = "Bob"; // Не даст изменить из-за final
}
void main() {
int num = 5;
var lst = [1, 2, 3];
String text = "hello";
var p1 = Person1("John1");
var p2 = Person2("John2");
modifyValues(num, lst, text, p1, p2);
print(num); // 5
print(lst); // [1, 2, 3, 4]
print(text); // "hello"
print(p1.name); // "Bob"
print(p2.name); // "John2"
modifyValues2(num, lst, text, p1, p2);
print(num); // 5
print(lst); // [1, 2, 3, 4, 8]
print(text); // "hello"
print(p1.name); // "Bib"
print(p2.name); // "John2"
List<int> list1 = [1, 2, 3];
List<int> list2 = list1; // list2 теперь ссылается на тот же список, что и list1
list1.add(4); // Изменяем список, на который ссылается list1 (и list2)
print(list1); // [1, 2, 3, 4]
print(list2); // [1, 2, 3, 4] (list2 тоже изменился!)
}
В Dart всегда передаются ссылки. Однако, из-за неизменяемости базовых типов, поведение при работе с ними похоже на передачу по значению. Важно понимать разницу и помнить, что при работе с иммутабельными объектами изменения, сделанные через одну переменную, отразятся на всех переменных, ссылающихся на тот же объект. Чтобы обезопасить себя запомните следующее:
// Делайте копию, если не хотите чтобы мутация отразилась на всех ссылках
var newList = List.from(originalList);
var newMap = Map.from(originalMap);
// Делайте глубокую копию (нужно реализовать самостоятельно),
// если не хотите чтобы мутация отразилась на всех ссылках
var deepCopyList = originalList.deepCopy().toList();
var deepMap = originalMap.deepCopy();
// Используйте UnmodifiableListView/UnmodifiableMapView для getter
List get l => List.unmodifiable(originalList);
Map get m => Map.unmodifiable(originalList);
// Думайте о передаче данных в конструкторах
class Box {
// Сохраняет ссылку на переданный список
const Box(this.items);
// Безопасный вариант - создание копии
const Box.safe(List<int> items) : items = List.from(items);
final List<int> items;
}
// Используйте иммутабельные объекты
class Address {
const Address(this.street);
final String street;
Address clone() {
return Address(street);
}
}
// Чтобы модифицировать иммутабельные объекты используйте clone/copyWith
class User {
const User(this.name, this.address);
final String name;
final Address address;
User clone() {
return User(name, address.clone());
}
User copyWith({final String? name, final Address? address}) {
return User(name ?? this.name, address ?? this.address.clone());
}
}