Dart에서 함수는 일급 객체(First-class Object)로, 변수에 담거나 다른 함수의 매개변수로 전달할 수 있습니다.
익명 함수와 화살표 함수를 단계적으로 살펴보겠습니다.
익명 함수(Anonymous Function)
익명 함수란?
익명 함수는 이름이 없는 함수로, 특정 작업을 수행하기 위해 일회성으로 사용되거나 변수에 담아 재활용할 수 있습니다. Dart에서는 간단한 문법으로 익명 함수를 정의할 수 있습니다.
(매개변수) {
// 수행 구문
};
코드 단계별 설명
1단계: 이름 없는 함수 정의
(int number) {
return 100 + number;
};
- Dart에서는 이름이 없는 함수(익명 함수)를 선언할 수 있습니다.
- 하지만, 익명 함수는 변수에 담지 않으면 호출할 방법이 없습니다.
2단계: 함수를 변수에 담기
Function() square = () {
return 10 * 10;
};
- square 변수에 함수를 담았습니다.
- Dart에서 함수의 타입은 Function입니다.
- square()를 호출하면 10의 제곱을 반환합니다.
3단계: 매개변수가 있는 익명 함수
var sub = (int number1, int number2) {
return number1 - number2;
};
- 두 매개변수를 받아 뺄셈을 수행하는 익명 함수입니다.
- sub(10, 5)처럼 호출할 수 있습니다.
4단계: 매개변수 타입 생략 가능
var add = (number1, number2) {
return number1 + number2;
};
- Dart에서는 매개변수 타입을 생략할 수 있어 코드가 더 간결해집니다.
연습 문제: 두 수의 곱하기
var mul = (a, b) {
return a * b;
};
- 두 수를 곱하는 익명 함수를 정의했습니다.
void main() {
Function() square = () {
return 10 * 10;
};
var sub = (int number1, int number2) {
return number1 - number2;
};
var add = (number1, number2) {
return number1 + number2;
};
var mul = (a, b) {
return a * b;
};
print("2의 제곱은 ::: ${square()}");
print("두 수의 빼기 연산 ::: ${sub(10, 5)}");
print("두 수의 더하기 연산 ::: ${add(10, 10)}");
print("두 수의 곱하기 연산 ::: ${mul(10, 5)}");
}
화살표 함수(Arrow Function)
화살표 함수란?
Dart에서 화살표 함수는 함수를 간결하게 표현하기 위한 문법입니다.
다른 언어의 람다 함수(Lambda Expression)와 유사하며, => 키워드를 사용합니다.
(매개변수) => 반환값;
- 함수의 본문이 단일 표현식이라면 화살표 함수로 간단히 정의할 수 있습니다.
코드 단계별 설명
1단계: 일반 함수와 비교
int add(int a, int b) {
return a + b;
}
// 화살표 함수로 변환
int addArrow(int a, int b) => a + b;
- add 함수는 일반적인 함수 정의입니다.
- addArrow는 동일한 동작을 화살표 함수로 간결하게 표현한 것입니다.
2단계: 화살표 함수 활용
int add2(int n1, int n2) => n1 + n2;
int sub(int n1, int n2) => n1 - n2;
int mul(n1, n2) => n1 * n2;
int div(n1, n2) => n1 / n2;
- 덧셈, 뺄셈, 곱셈, 나눗셈을 수행하는 함수들을 화살표 함수로 정의했습니다.
- 매개변수의 타입을 생략해 더 간결하게 작성할 수도 있습니다.
연습 문제: 원과 직사각형의 면적 계산
void main() {
print('원의 반지름 5의 면적 : ${circle(5)}');
print('직사각형 가로 3.6, 세로 4의 면적 : ${rectangle(3.6, 4)}');
}
double circle(double n1) => n1 * n1 * 3.14; // 원의 면적 계산
double rectangle(double n1, double n2) => n1 * n2; // 직사각형 면적 계산
- circle 함수는 원의 면적을 계산합니다.
- rectangle 함수는 직사각형의 면적을 계산합니다.
클래스 만들어 보기
클래스란?
클래스는 객체(인스턴스)를 정의하는 설계도입니다. 연관되어 있는 변수와 메서드의 집합.
우리가 집을 짓기 위해 도면을 그리는 것처럼, 클래스를 통해 프로그램에서 다룰 객체의 구조와 동작을 설계할 수 있습니다.
객체란?
객체는 클래스를 기반으로 생성된 실체를 말합니다.
소프트웨어 세계에 구현할 대상, 객체는 모든 인스턴스를 대표하는 포괄적인 의미를 갖는다.
예를 들어, Dog 클래스는 강아지라는 객체의 설계도입니다.
이 클래스를 기반으로 만들어진 d1은 실제로 프로그램에서 동작하는 강아지 객체가 됩니다.
인스턴스(Instance)
설계도를 바탕으로 소프트웨어 세계에 구현된 구체적인 실체 즉, 객체를 소프트웨어에 실체화 하면 그것을 ‘인스턴스’라고 부른다.
실체화된 인스턴스는 메모리에 할당된다.
객체 지향 프로그래밍(OOP)이란?
객체 지향 프로그래밍은 객체와 객체 간의 관계를 형성하고, 이들의 상호작용을 통해 애플리케이션을 설계하고 구현하는 프로그래밍 패러다임입니다.
이 방법은 복잡한 문제를 더 작은 단위(객체)로 나누어 해결함으로써, 코드의 재사용성, 유지보수성, 확장성을 높이는 데 초점을 둡니다.
void main() {
Dog d1 = Dog(); // 강아지 객체 생성 (new 키워드 생략 가능)
print('강아지 이름: ${d1.name}');
print('강아지 나이: ${d1.age}');
print('강아지 색상: ${d1.color}');
print('강아지 현재 갈증 지수: ${d1.thirsty}');
d1.thirsty = 50; // 속성 값 수정
print('강아지 현재 갈증 지수: ${d1.thirsty}');
}
class Dog {
String name = '토토'; // 초기값 설정
int age = 5;
String color = '블랙';
int thirsty = 100; // 갈증 지수
}
- Dog 클래스는 강아지의 속성을 정의한 설계도입니다.
- thirsty 속성은 강아지의 갈증 정도를 나타내며, 객체 생성 시 기본값은 100입니다.
- 객체는 클래스의 속성을 통해 상태를 저장하고 관리할 수 있습니다.
메서드 만들어 보기
메서드란?
메서드는 객체의 행위를 정의
예를 들어, 강아지가 물을 마시는 행동을 메서드로 정의하면, 이 동작을 통해 객체의 상태(속성)가 변경됩니다.
void main() {
Dog d1 = Dog(); // 강아지 객체 생성
d1.drinkWater(); // 물을 마시는 메서드 호출
print('강아지 현재 갈증 지수: ${d1.thirsty}');
}
class Dog {
String name = '토토';
int age = 5;
String color = '블랙';
int thirsty = 100;
// 물을 마시면 갈증 지수가 10 감소
void drinkWater() {
thirsty -= 10;
}
}
- drinkWater 메서드는 갈증 지수를 10씩 줄이는 동작을 정의합니다.
- 객체의 메서드를 호출하면, 객체 내부 상태가 변화합니다.
클래스 간 협력
클래스 간 협력
클래스 간 협력은 객체가 서로 상호작용하며 작업을 수행하는 과정입니다.
예를 들어, 강아지가 물을 마신다면, 물 객체의 양이 줄어드는 것을 함께 구현할 수 있습니다.
void main() {
Dog d1 = Dog();
Water w1 = Water();
d1.drinkWater(w1); // 강아지가 물을 마심
print('남은 물의 양: ${w1.liter}L');
}
class Dog {
String name = '토토';
int thirsty = 100;
void drinkWater(Water water) {
thirsty -= 10;
water.drink(); // Water 클래스의 메서드 호출
}
}
class Water {
double liter = 2.0; // 초기 물의 양
void drink() {
liter -= 0.1; // 물이 줄어드는 동작
}
}
- Dog 클래스는 Water 클래스와 상호작용합니다.
- 강아지가 물을 마실 때, 강아지의 갈증 지수는 감소하고 물의 양도 줄어듭니다.
생성자
생성자란?
생성자는 객체를 생성할 때 초기값을 설정 (= 인스턴스 초기화) 하는 특별한 메서드입니다.
Dart에서는 this 키워드를 통해 클래스의 속성을 생성자에서 초기화할 수 있습니다.
*기본 생성자: 매개변수가 없는 생성자 - 초기값이 존재하면 초기값, 없으면 null
class Dog {
String name;
int age;
String color;
int thirsty;
Dog(this.name, this.age, this.color, this.thirsty); // 생성자
}
- Dog 생성자는 객체 생성 시 필요한 값을 받아 속성을 초기화합니다.
- Dart는 생성자 오버로딩을 지원하지 않으므로, 선택적 매개변수를 활용합니다.
선택적 명명 매개변수
선택적 명명 매개변수
Dart에서는 선택적 명명 매개변수를 통해 특정 속성만 초기화할 수 있습니다. { }
필수 속성에는 required 키워드를, 선택 속성에는 기본값을 지정할 수 있습니다.
class Dog {
String name;
int age;
String? color; // null 허용
int? thirsty;
Dog({
required this.name,
required this.age,
this.color = '블랙', // 기본값
this.thirsty = 100,
});
}
- 선택적 매개변수를 통해 객체 생성 시 필요한 속성만 지정할 수 있습니다.
캐스케이드 연산자
캐스케이드 연산자(..)란?
캐스케이드 연산자는 하나의 객체에서 여러 메서드와 속성을 연속적으로 호출할 때 유용합니다.
이 방식은 코드 가독성을 높이고, 객체를 간결하게 초기화할 수 있도록 돕습니다.
void main() {
var p1 = Person()
..setName('홍길동')
..addMoney(5000)
..money += 2000; // 속성 직접 변경
print('이름: ${p1.name}');
print('잔고: ${p1.money}');
}
class Person {
String? name;
int money = 0;
void setName(String newName) {
this.name = newName;
}
void addMoney(int amount) {
this.money += amount;
}
}
- .. 연산자를 통해 동일 객체에서 여러 작업을 수행할 수 있습니다.
- 객체 초기화 및 수정 작업을 간결하게 표현합니다.
정리
- 익명 함수는 이름 없이 정의되어 간단한 작업을 수행하거나 변수에 담아 재활용할 수 있습니다.
- 화살표 함수는 단일 표현식 함수를 간결하게 작성하는 데 유용합니다.
- Dart는 함수 정의와 활용이 간단하면서도 강력한 유연성을 제공합니다.
Dart에서 클래스와 객체를 사용하는 방법은 객체 지향 프로그래밍의 기본 개념을 충실히 따르고 있습니다.
클래스와 객체 간의 상호작용, 생성자, 선택적 매개변수, 그리고 캐스케이드 연산자와 같은 Dart 고유의 특징을 활용하면 더 효율적이고 유지보수성이 높은 코드를 작성할 수 있습니다.