일단, Object equality를 비교하기 전에 Dart의 타입 시스템, 특히
final과const의 차이를 명확히 이해하는 것이 중요하다. Dart 문법 공부 처음할 땐 대충 넘겼지만ㅎㅎ(뭐.. 하다보니 깨달을 수도 있는거죠..그냥 api 받아와서 UI그리면 그만인 줄 알았어요)
List나 State로 관리할 때, 객체의 값은 같은데 == 비교가 false가 나오면?setState() 발생, 위젯 리빌드, 캐시 미스 등의 문제가 생긴다.값만 같아선 안 되고, 객체 비교 기준을 직접 정의해야 한다.| 구분 | final | const |
|---|---|---|
| 선언 시점 | 런타임에 결정됨 | 컴파일 타임에 결정됨 |
| 인스턴스 생성 | 매번 새 객체 | 같은 값이면 재사용 (싱글턴처럼) |
| 메모리 | 각각 따로 있음 | 공유됨 (정적 메모리로 할당됨) |
동일성 (== 또는 identical) | 값이 같아도 false | 값이 같으면 true |
다음과 같은 예제를 살펴보자
- 먼저
Person객체를 정의한다.Person클래스의 property는id,name,
// ignore_for_file: public_member_api_docs, sort_constructors_first
class Person {
final int id;
final String name;
final String email;
Person({required this.id, required this.name, required this.email});
String toString() => 'Person(id: $id, name: $name, email: $email)';
Person copyWith({int? id, String? name, String? email}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
);
}
}
import 'package:dart_class/models/person.dart';
import 'package:flutter/material.dart';
class PersonPage extends StatelessWidget {
const PersonPage({super.key});
Widget build(BuildContext context) {
final person1 = Person(id: 1, name: 'John', email: 'john@gmail.com');
final person2 = person1.copyWith(id: 2, email: 'Johndoe@gmail.com');
final person3 = Person(id: 1, name: 'John', email: 'john@gmail.com');
print(person1);
print(person2);
print(person1 == person3);
print(person1.hashCode);
print(person3.hashCode);
return Scaffold(appBar: AppBar(title: Text('Person')));
}
}
출력 결과:
false
949309463
581349880
person1과 person3의 property는 같은 상태이다. 하지만 equality 연산자의 값이 false임을 확인할 수 있다.person1과 person3의 hash값이 다른걸 보아,, 다른 메모리에 할당되었음을 알 수 있다.const Person({required this.id, required this.name, required this.email});
import 'package:dart_class/models/person.dart';
import 'package:flutter/material.dart';
class PersonPage extends StatelessWidget {
const PersonPage({super.key});
Widget build(BuildContext context) {
const person1 = Person(id: 1, name: 'John', email: 'john@gmail.com');
final person2 = person1.copyWith(id: 2, email: 'Johndoe@gmail.com');
const person3 = Person(id: 1, name: 'John', email: 'john@gmail.com');
print(person1);
print(person2);
print(person1 == person3);
print(person1.hashCode);
print(person3.hashCode);
return Scaffold(appBar: AppBar(title: Text('Person')));
}
}
true 423981177 // person 1 == person3의 논리적 equality

vscode extension -> 객체 생성 도와주는 Tool

Generate equality클릭
// ignore_for_file: public_member_api_docs, sort_constructors_first
class Person {
final int id;
final String name;
final String email;
const Person({required this.id, required this.name, required this.email});
String toString() => 'Person(id: $id, name: $name, email: $email)';
Person copyWith({int? id, String? name, String? email}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
);
}
bool operator ==(covariant Person other) {
if (identical(this, other)) return true;
return other.id == id && other.name == name && other.email == email;
}
int get hashCode => id.hashCode ^ name.hashCode ^ email.hashCode;
}
equatable 패키지 사용== 연산자와 hashCode를 직접 override하는 대신,Equatable을 상속받고 props만 정의해주면 된다.props 리스트에 들어간 값들을 기준으로 == 비교와 hashCode 생성을 자동 처리한다.import 'package:equatable/equatable.dart';
// ignore_for_file: public_member_api_docs, sort_constructors_first
class Person extends Equatable {
final int id;
final String name;
final String email;
const Person({required this.id, required this.name, required this.email});
String toString() => 'Person(id: $id, name: $name, email: $email)';
Person copyWith({int? id, String? name, String? email}) {
return Person(
id: id ?? this.id,
name: name ?? this.name,
email: email ?? this.email,
);
}
List<Object> get props => [id, name, email];
}
List<Object> get props => [id];
로 정의하면, id이 같지만 나머지 property가 다른 객체들의 동등성이 부여된다.!

쉬운게 하나도 없다.