[Flutter] Dart에서 Generic을 사용할 때, 런타임에서 타입을 확인해보자.

2021. 5. 28. 11:01Programming/Flutter

반응형

Dart에서 Generic을 사용할 때, 런타임에서 타입을 확인해보자.

종종 제네릭을 사용할 때 타입을 확인해야 할 때가 있다. 보통 나는 SharedPreferencesJSONObject의 get함수 시리즈를 묶어버릴 때 사용하고는 한다. Kotlin에서는 Kotlin에서 JSONObject .get*의 확장 함수를 만들어봤다.와 같은 방식으로, reified를 사용해서 타입에 따른 분기를 처리했다. 아무래도 getString, getInt, gettBoolean등의 함수로 작성하면, 코드를 작성하다가 타입을 변경하기 영 귀찮았기 때문이다. 제네릭을 사용해서 함수를 이렇게 래핑하면 타입추론에 의지할수도 있어서, 아무래도 편리하다. 항상 타입추론을 맹신할 수는 없겠지만, 아무튼.

Dart의 Type 클래스

Dart에는 Type 클래스라는 녀석이 있다. 문서를 보면 런타임에서의 타입을 나타내는 클래스라는 걸 알 수 있다.

Runtime representation of a type.

Type objects represent types. A type object can be created in several ways:

  • By a type literal, a type name occurring as an expression, like Type type = int;, or a type variable occurring as an expression, like Type type = T;.
  • By reading the run-time type of an object, like Type type = o.runtimeType;.
  • Through dart:mirrors.

A type object is intended as an entry point for using dart:mirrors. The only operations supported are comparing to other type objects for equality, and converting it to a string for debugging.

 

이 녀석을 사용하면 Kotlin의 Reified키워드를 사용할 때처럼 런타임에서의 타입을 추론할 수 있다. 다음의 스택오버플로우 글 How to get generic Type?을 살펴보면, Xavier가 달아놓은 답변에서 Type 클래스를 사용해서 런타임의 타입을 반환하는 함수를 확인할 수 있다.

Type typeOf<T>() => T;

제네릭을 사용하는 함수 내부에서 실행하면 주어진 인자 T의 타입을 반환해주는 한 줄짜리 함수이다. 이것을 사용하면 런타임에서의 타입에 따른 분기처리가 가능해진다. 이것으로 getString, getInt, getBoolean...같은 함수들을 제네릭으로 함수 하나로 묶을 수 있게됐다.

 

최신 버전의 SDK에서는 Type typeOf() => T; 함수를 만들지 않아도, 그냥 T를 비교하면 런타임의 타입을 비교할 수 있다. 예를 들어, 변수 T의 타입이 String인지 if문 내에서 체크할 경우 if(T == String){}를 호출하면 된다.

SharedPreferences의 get 시리즈 함수를 Generic으로 묶어보자.

여기서는 shared_preferences 패키지를 사용하여 SharedPreferences를 사용하되, getString, getInt, getBoolean 등의 함수를 하나의 제네릭 함수 fetch로 묶어볼 것이다.

Future<T> fetch({
  SharedPreferences sharedPreferences
}) async {
      T result;

      if (sharedPreferences == null) {
        sharedPreferences = await SharedPreferences.getInstance();
      }

      switch (T) {
        case String:
          result = sharedPreferences.getString(key) as T;
          break;
        case bool:
          result = sharedPreferences.getBool(key) as T;
          break;
        case double:
          result = sharedPreferences.getDouble(key) as T;
          break;
        case int:
          result = sharedPreferences.getInt(key) as T;
          break;
        case List:
          result = sharedPreferences.getStringList(key) as T;
          break;
        default:
          result = sharedPreferences.get(key);
          break;
      }

      return result;
    };

위처럼 작성하면 getString, getBool, getDouble, getInt, getStringList를 하나의 fetch함수로 호출할 수 있게된다. set도 마찬가지로 처리해주면 된다. 물론, 아직도 SharedPreferences를 사용할 때 귀찮은 점이 남아있긴하다. 키 값이 문자열이므로 코드상에 키값을 직접 입력하는 경우 오타가 발생할 수 있다는 점과, 값을 읽고 쓸 때 타입을 서로 다르게 지정할 수 있다는 점 정도이다. 다음번엔 이 귀찮은 점을 해결하기 위해, SharedPreferencesHelper를 만들어보도록 하자. ' ㅈ')/

반응형