본문 바로가기
프로그래밍/Dart

[Dart] 10. Dart의 "고급 주제"

by iwbap 2024. 6. 19.
728x90

Dart 학습: 고급 주제

Dart의 고급 기능을 활용하면 더 강력하고 유연한 코드를 작성할 수 있습니다. 이번 글에서는 제너릭, 믹스인, 메타프로그래밍(리플렉션과 어노테이션)에 대해 알아보겠습니다.


1. 제너릭: 제너릭 클래스와 함수

제너릭(Generics)은 데이터 타입을 일반화하여 코드 재사용성을 높이는 방법입니다. 제너릭을 사용하면 타입 안정성을 유지하면서 다양한 데이터 타입을 처리할 수 있습니다.

 

제너릭 클래스: 제너릭 클래스를 사용하면 클래스가 다양한 데이터 타입을 처리할 수 있습니다.

[dart]
 
class Box<T> {
  T value;

  Box(this.value);

  void display() {
    print('Value: $value');
  }
}

void main() {
  Box<int> intBox = Box<int>(123);
  intBox.display();  // Value: 123 출력

  Box<String> strBox = Box<String>('Hello, Dart');
  strBox.display();  // Value: Hello, Dart 출력
}
 

위의 예제에서 Box 클래스는 제너릭 타입 T를 사용하여 다양한 데이터 타입을 처리할 수 있습니다. int와 String 타입의 Box 객체를 각각 생성하여 사용합니다.

 

 

제너릭 함수: 제너릭 함수를 사용하면 함수가 다양한 데이터 타입을 처리할 수 있습니다.

[dart]
 
T add<T extends num>(T a, T b) {
  return a + b;
}

void main() {
  print(add<int>(2, 3));      // 5 출력
  print(add<double>(2.5, 3.5));  // 6.0 출력
}
 

위의 예제에서 add 함수는 제너릭 타입 T를 사용하여 숫자 타입의 값을 더할 수 있습니다. int와 double 타입의 값을 더하는 데 사용됩니다.


2. 믹스인: 믹스인 사용법

믹스인(Mixin)은 여러 클래스의 기능을 한 클래스에 혼합할 수 있는 방법입니다. 믹스인은 상속과 유사하지만, 상속보다 더 유연하게 사용할 수 있습니다.

 

믹스인 사용 예제:

[dart]
 
mixin Fly {
  void fly() {
    print('Flying');
  }
}

mixin Swim {
  void swim() {
    print('Swimming');
  }
}

class Duck with Fly, Swim {
  void display() {
    print('I am a duck');
  }
}

void main() {
  Duck duck = Duck();
  duck.display();  // I am a duck 출력
  duck.fly();      // Flying 출력
  duck.swim();     // Swimming 출력
}
 

위의 예제에서 Fly와 Swim 믹스인은 각각 fly와 swim 메서드를 제공합니다. Duck 클래스는 with 키워드를 사용하여 두 믹스인을 혼합하여 사용할 수 있습니다.


3. 메타프로그래밍: 리플렉션과 어노테이션

메타프로그래밍은 프로그램이 자신의 구조나 동작을 검사하고 수정할 수 있는 프로그래밍 기법입니다. Dart에서는 리플렉션(reflection)과 어노테이션(annotation)을 사용하여 메타프로그래밍을 구현할 수 있습니다.

 

리플렉션: 리플렉션은 객체의 타입, 메서드, 필드 등을 런타임에 검사하고 조작할 수 있는 기능입니다. Dart에서는 dart:mirrors 라이브러리를 사용하여 리플렉션을 구현할 수 있습니다.

 

리플렉션 예제:

[dart]
 
import 'dart:mirrors';

class Person {
  String name;
  int age;

  Person(this.name, this.age);

  void greet() {
    print('Hello, my name is $name and I am $age years old.');
  }
}

void main() {
  Person person = Person('Alice', 25);

  // 리플렉션을 사용하여 객체의 메서드 호출
  InstanceMirror im = reflect(person);
  ClassMirror cm = im.type;

  cm.declarations.forEach((symbol, declaration) {
    if (declaration is MethodMirror && declaration.simpleName == Symbol('greet')) {
      im.invoke(declaration.simpleName, []);
    }
  });
}
 

위의 예제에서 reflect 함수를 사용하여 객체의 인스턴스를 검사하고, invoke 메서드를 사용하여 greet 메서드를 런타임에 호출합니다.

 

 

어노테이션: 어노테이션은 메타데이터를 클래스, 메서드, 필드 등에 추가할 수 있는 기능입니다. Dart에서는 @ 기호를 사용하여 어노테이션을 정의할 수 있습니다.

 

어노테이션 예제:

[dart]
 
class MyAnnotation {
  final String description;
  const MyAnnotation(this.description);
}

@MyAnnotation('This is a person class')
class Person {
  String name;
  int age;

  Person(this.name, this.age);

  @MyAnnotation('This is a greet method')
  void greet() {
    print('Hello, my name is $name and I am $age years old.');
  }
}

void main() {
  // 어노테이션을 리플렉션을 통해 검사
  ClassMirror cm = reflectClass(Person);

  cm.metadata.forEach((meta) {
    MyAnnotation annotation = meta.reflectee as MyAnnotation;
    print('Class annotation: ${annotation.description}');
  });

  cm.declarations.forEach((symbol, declaration) {
    if (declaration is MethodMirror) {
      declaration.metadata.forEach((meta) {
        MyAnnotation annotation = meta.reflectee as MyAnnotation;
        print('Method annotation: ${annotation.description}');
      });
    }
  });
}
 

위의 예제에서 MyAnnotation 어노테이션을 정의하고, Person 클래스와 greet 메서드에 어노테이션을 추가합니다. reflectClass 함수를 사용하여 어노테이션을 검사하고 출력합니다.


이 글에서는 Dart의 고급 주제인 제너릭, 믹스인, 메타프로그래밍(리플렉션과 어노테이션)에 대해 알아보았습니다. 이러한 고급 기능을 이해하고 활용하면 더 강력하고 유연한 코드를 작성할 수 있습니다. Happy Coding!

728x90