티스토리 뷰
확장 메서드는 기존 라이브러리에 기능을 추가하는 방법이다.
예를 들어, 다음 코드는 문자열을 정수로 변환하는 코드가 된다.
int.parse('42')
이 기능은 String 클래스에 있으면 더 좋을 수 있다.
'42'.parseInt()
확장 메서드는 메서드뿐만 아니라 게터, 세터, 그리고 연산자와 같은 다른 멤버도 정의할 수 있다.
다음은 문자열에서 동작하는 확장을 사용하여 parseInt() 확장 메서드를 구현하는 방법이다.
// string_apis.dart
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
string_apis.dart에 만들어 놓은 parseInt를 활성화하려면, 해당 라이브러리를 가져오면 된다.
import 'string_apis.dart';
// 확장 메서드 사용
print('42'.parseInt());
확장 메서드 사용하기
확장 메서드를 사용하는 방법은 확장 메서드가 작성된 라이브러리르 가져오고 일반 메서드처럼 사용하면 된다.
import 'string_apis.dart';
print('42'.padLeft(5)); // String 메서드 사용
print('42'.parseInt()); // 확장 메서드 사용
정적 타입과 동적 타입
정적 타입이 아닌 동적 타입에서는 확장 메서드를 호출할 수 없다. 예를 들어 다음 코드는 런타임 에러가 발생한다.
dynamic d = '2';
print(d.parseInt()); // 런타임 예러: NoSuchMethodError
확장 메서드는 다트의 타입 추론과 함께 작동한다. 다음 코드는 변수 v가 String 타입으로 추론되기 때문에 확장 메서드를 사용해도 문제없이 작동한다.
var v = '2';
print(v.parseInt()); // 출력: 2
동적 타입에서 확장 메서드가 작동하지 않는 이유는 확장 메서드가 정적 타입인 경우에만 추론이 가능하기 때문이다. 확장 메서드는 정적으로 해결되므로 일반 메서드 호출만큼 빠르게 작동하게 된다.
API 충돌
확장 멤버가 인터페이스나 다른 확장 멤버와 충돌하는 경우, 몇 가지 방법으로 해결할 수 있다.
하나는 충돌하는 확장을 가져오는 방식을 변경하는 것이다. show 또는 hide를 사용하면 노출되는 API를 제한할 수 있다.
// String 확장 메서드 parseInt() 정의
import 'string_apis.dart';
// parseInt()도 정의되지만, NumberParsing2를 숨기므로
// 해당 확장 메서드는 숨겨집니다.
import 'string_apis_2.dart' hide NumberParsing2;
// ···
// 'string_apis.dart'에서 정의된 parseInt() 사용
print('42'.parseInt());
또 다른 방법은 확장을 명시적으로 적용하는 것이다. 이는 확장이 클래스처럼 보이는 코드가 된다.
// 두 라이브러리가 모두 String에 대한 확장 정의
// 그리고 확장 이름이 다릅니다.
import 'string_apis.dart'; // NumberParsing 확장 포함
import 'string_apis_2.dart'; // NumberParsing2 확장 포함
// ···
// print('42'.parseInt()); // 작동하지 않음
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
두 확장에 동일한 이름이 있는 경우, 접두사를 사용하여 가져올 수 있다.
// 두 라이브러리 모두 parseInt()를 포함하는 NumberParsing이라는
// 확장을 정의하고 있습니다. 하나의 NumberParsing
// 확장('string_apis_3.dart'에 있는)은 또한 parseNum()을 정의합니다.
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;
// ···
// print('42'.parseInt()); // 작동하지 않음
// 'string_apis.dart'의 ParseNumbers 확장 사용
print(NumberParsing('42').parseInt());
// 'string_apis_3.dart'의 ParseNumbers 확장 사용
print(rad.NumberParsing('42').parseInt());
// parseNum()은 오직 'string_apis_3.dart'에만 있습니다.
print('42'.parseNum());
예제에서 보듯이, 접두사를 사용하여 가져오더라도 유일한 경우에는 접두사를 사용하지 않고 확장 메서드를 호출할 수 있다. 접두사를 사용하는 이유는 확장을 명시적으로 호출하여 이름 충돌을 피하기 위해서이다.
확장 메서드 구현하기
다음 구문을 사용하여 확장을 생성한다.
extension <extension name>? on <type> { // <extension-name>은 선택 사항입니다
(<member definition>)* // 하나 이상의 <member definition>을 제공할 수 있습니다.
}
예를 들어, String 클래스에 대한 확장을 구현하는 방법은 다음과 같다.
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
확장의 멤버는 메서드, 게터, 세터 또는 연산자가 될 수 있다. 확장에는 정적 필드와 정적 도우미 메서드도 가질 수 있다.
이름 없는 확장
확장을 선언할 때, 이름을 생략할 수 있다. 이름이 없는 확장은 선언된 라이브러리 내에서만 사용할 수 있다. 이름이 없는 확장은 API 충돌이 발생하면 해결할 수 있는 방법이 없기 때문에 확장 API를 수정해야 한다.
extension on String {
bool get isBlank => trim().isEmpty;
}
제네릭 확장 구현하기
확장에는 제네릭 타입 매개변수를 가질 수 있다.
예를 들어, 다음은 내장 List<T> 타입을 확장하여, 게터, 연산자 및 메서드를 추가하는 코드이다. 타입 T는 메서드가 호출되는 리스트의 정적 타입에 기반하여 바인딩된다.
extension MyFancyList<T> on List<T> {
int get doubleLength => length * 2;
List<T> operator -() => reversed.toList();
List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}
'다트 공식 문서 번역' 카테고리의 다른 글
다트] 호출 가능한 객체 (0) | 2024.08.07 |
---|---|
다트] 확장 타입 (0) | 2024.08.05 |
다트] 열거형 타입 (enum) (0) | 2024.08.04 |
다트] 믹스인 (0) | 2024.08.04 |
다트] 클래스를 확장하기 (0) | 2024.08.03 |