저번 시간에는 Flutter에서의 Key의 개념과, 그 하위 클래스 중 하나인 GlobalKey에 대해서 알아보았다.
이번 시간에는 Key의 나머지 하위 클래스인 LocalKey에 대해서 알아보도록 하자.
2. LocalKey
Key 클래스 중에서 GlobalKey를 제외한 나머지는 전부 LocalKey에 해당한다.
A key that is not a [GlobalKey].
LocalKey에 달려 있는 얼마 안 되는 주석 중에서, 다음 항목을 보며 저번 시간에 대한 복습을 하도록 하자.
Keys must be unique amongst the [Element]s with the same parent. By contrast, [GlobalKey]s must be unique across the entire app.
GlobalKey는 앱 전체에 걸쳐 유일해야 하고, 나머지 Key(LocalKey)는 같은 부모를 가진 형제 사이에서 유일해야 한다는 것을 알 수 있다.
LocalKey의 하위 클래스들
LocalKey의 하위 클래스로는 다음 세 가지가 있다. 혹시 더 있을 수도 있지만, 눈에 띄지 않는다는 건 쓸 일이 없다는 것이므로 무시하도록 한다.
- UniqueKey
- ValueKey
- ObjectKey
세 종류의 Key에 대해 각각 살펴보겠다.
2-1. UniqueKey
A key that is only equal to itself
UniqueKey는 오직 자기 자신과만 같다고 판별되는 key이다. 따라서 UniqueKey는 const 생성자를 갖지 않는다. const 생성자를 통해 생성된 인스턴스들은 전부 같은 인스턴스로 간주되기 때문이다.
막간 상식!
const 생성자를 통해 생성된 인스턴스는 마치 '상수 리터럴' 처럼 동작합니다.
같은 부모를 가진 여러 Container들 중에서 유일성을 보장하는 개체가 존재한다면, 해당 위젯의 key에 인스턴싱된 UniqueKey를 할당해서 사용하다가, 순서가 바뀌게 됐을 때 다시 해당 key 인스턴스를 할당하는 방식으로 사용할 수 있다. 하지만 대체로 특정 값을 넣을 수 있는 ValueKey를 사용하는 경우가 더 많으리라 생각된다.
2-2. ValueKey
A [ValueKey<T>] is equal to another [ValueKey<T>] if, and only if, their values are [operator==].
ValueKey는 할당된 T 타입의 인스턴스의 == 연산자가 참일 경우에만 다른 ValueKey와 동일하다고 판단된다. 따라서 ValueKey의 value로 사용자 정의 클래스 타입의 인스턴스를 사용하고 싶다면, 반드시 해당 클래스에 == 연산자를 override해야 한다.
사용자 정의 클래스를 Map의 key로 사용할 때 == 연산자를 재정의하는 경우가 많다.
2-3. ObjectKey
ObjectKey는 얼핏 보면 ValueKey와 다를 게 없어 보인다. 그러나 동일하다고 판단하는 기준에서 미묘한 차이가 있다.
// ValueKey
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is ValueKey<T>
&& other.value == value;
}
// ObjectKey
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is ObjectKey
&& identical(other.value, value);
}
물론 ObjectKey는 value로 Object 타입을 받고, ValueKey는 T 타입을 받는다는 차이가 있기는 하지만, 실질적으로 큰 의미는 없다. 반면에 ObjectKey와 ValueKey에서 == 연산자를 재정의한 내용을 보면 실질적으로 두 LocalKey 중에서 선택을 해야 할 때 어떤 부분을 고려해야 하는지 알 수 있다.
우선, ValueKey는 other.value와 this.value의 == 연산의 결과를 본인의 == 연산의 결과로 사용하고 있다. 한편 ObjectKey는 other.value와 this.value의 identical 여부를 == 연산의 결과로 사용하고 있는데, 이 identical 함수의 선언부에는 다음과 같은 주석이 달려 있다.
var o = new Object();
var isIdentical = identical(o, new Object()); // false, different objects.
isIdentical = identical(o, o); // true, same object
isIdentical = identical(const Object(), const Object()); // true, const canonicalizes
isIdentical = identical([1], [1]); // false
isIdentical = identical(const [1], const [1]); // true
isIdentical = identical(const [1], const [2]); //false
isIdentical = identical(2, 1 + 1); // true, integers canonicalizes
해당 주석에서 확인할 수 있다시피, identical 함수는 비교하려는 두 Object가 완전히 동일한지 판단한다(js의 === 과 유사하다). 반면에 == 연산은 일반적으로 두 Object의 '값'이 동일한지 판단한다. 물론 == 연산은 재정의가 가능하기에 == 연산의 결과를 identical 함수의 결과와 동일하게 만들어줄 수도 있다. 어쨌거나, 만약 LocalKey의 특징을 갖는 Key가 필요한데 해당 Key의 동일성을 어떤 Object 인스턴스가 '완전히 동일한지' 여부로 판단하고 싶다면 ObjectKey를 사용하면 될 것 같다.
생각보다 LocalKey에 대한 주석이 많지 않아 짧은 글이 되어버렸다.