뚝딱뚝딱 모바일

[Flutter] Equatable에 대해 알아보자 본문

Flutter 지식

[Flutter] Equatable에 대해 알아보자

규석 2023. 12. 8. 14:36

오늘은 정말 자주 쓰이는 라이브러리인 Equatable에 대해 알아보겠습니다.

활용도가 정말 높은 라이브러리이니 알아만 두셔도 좋을 것 같습니다.


 

equatable | Dart Package

A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode.

pub.dev

다른 언어를 접해보셨던 분들이라면, Equatable은 언어 자체적으로 존재해야 되는 거 아닌가? 왜 라이브러리로 따로 존재하지? 싶으실 겁니다. Dart에서는 모든 클래스들은 Object를 상속하고, 이 Object에는 operator 함수가 존재해 override 함으로 수정할 수 있기 때문에, 언어 자체적으로 Equatable이 존재하진 않습니다. (참고로 일반적인 상황에서 operator 함수를 정의하지 않은 클래스들은 '==' 연산자로 비교해 주었을 때, 메모리 위치로 비교하게 됩니다.)

operator 함수 사용하여 구분하기

Equatable 라이브러리를 사용하기 전, operator 함수를 사용하여 객체를 구분해보겠습니다.

class Dog {
  String name;

  Dog({required this.name});

  @override
  bool operator ==(Object other) {
    return other is Dog && name == other.name;
  }
}

Dog 클래스를 만들었고, operator 함수를 override 해주었습니다. 그러고 함수 내부에서 other 인자를 Dog인지 확인해 주고 name으로 비교해 주었습니다. 비교해 볼까요?

Dog a = Dog(name: '뽀삐');
Dog b = Dog(name: '뽀삐');

print(a == b); // true

name이 같기에 true가 출력되고, 달라지면 false가 출력되는 것을 알 수 있습니다. 의도대로 잘 동작합니다.

그런데 == 함수 아래에 노란 물결표가 쳐지는 것을 알 수 있습니다.

hashCode?

hashCode 인자도 같이 override 하라고 합니다. 그게 아니면 == operator를 지우라고 하네요. 지우긴 싫으니 추가해 줍시다.

@override
int get hashCode => super.hashCode;

무엇을 하는 변수이기에, 같이 선언해야 되는 걸까요? hashCode는 Map이나 Set에서 Key의 역할을 하는 변수입니다. 그렇기에 int 타입입니다. 여기서 한 가지 의문점이 드실 겁니다.

== 함수에서는 String 타입인 name으로 구분했는데, hashCode는 그게 불가능하지 않나요?

 

네, 맞습니다. 따로 객체마다 int 타입의 값을 주는 것이 아니라면, Map과 Set에서 Key로 사용할 hashCode를 사용할 수 없습니다. 이러한 불편한 점을 개선하기 위해 나온 라이브러리가 Equatable입니다.

Equatable

먼저, pubspec.yaml에 추가해 줍시다.

equatable: ^2.0.5

pub get을 한 이후에 Dog 클래스에 Equatable을 상속하여 줍니다. 상속하면 props 변수를 override 하라 합니다.

class Dog extends Equatable {
  String name;

  Dog({required this.name});

  @override
  List<Object?> get props => [];
}

props의 요소로 값들을 넣어주시면, == 함수와 hashCode 변수를 만드는 데 사용됩니다. name을 넣어주고 다시 한번 비교해 줍시다.

class Dog extends Equatable {
  String name;

  Dog({required this.name});

  @override
  List<Object?> get props => [name];
}

void main(List<String> arguments) {
  Dog a = Dog(name: '뽀삐');
  Dog b = Dog(name: '뽀삐');

  print(a == b); // true
}

원하는 동작을 얻었습니다. Map에서도 확인해 보겠습니다.

Dog a = Dog(name: '뽀삐');
Dog b = Dog(name: '뽀삐');

final map = {
  a : 1,
  b : 2
};

print(map.length); // 1

a와 b의 name이 같아 같은 객체 취급을 받기 때문에, map의 길이가 1이 되었습니다.

 

또한, props 변수가 List이니, 여러 인자를 넣어 비교할 수도 있습니다.

class Dog extends Equatable {
  String name;
  int age;
  String kind;
  
  Dog({
    required this.name,
    required this.age,
    required this.kind
  });

  @override
  List<Object?> get props => [name, age, kind];
}
Dog a = Dog(name: '뽀삐', age: 2, kind: '말티즈');
Dog b = Dog(name: '뽀삐', age: 4, kind: '말티즈');

print(a == b); // false

name과 age, kind 모두를 비교함으로 false가 출력됩니다. 이렇게 본인이 비교군에 사용하고 싶은 인자를 props 리스트의 요소로 넣어주시면 됩니다.