1. ex06.dart
//클래스 안에 없으니까 기본함수
//add를 더 추가하고 싶으면 자바에선 오버로딩이라고 한다.
//하지만 dart는 오버로딩을 지원하지않는다.
void add(int n1, int n2) {
print(n1 + n2);
}
//Type생략 가능 -> Type추론(좋은 코드는 아니다.)
void add1(n1, n2) {
print(n1 + n2);
}
// return을 받는 것은 무조건 타입을 지정해줘야한다. var은 리턴 되지 않는다.
// Object타입은 받을 수 있다. dynamic은 가능하다.
int minus(n1, n2) {
return n1 = n2;
}
//원래코드 int multi(n1,n2)였다 하지만 function을 쓰려고하면
//익명함수로 바꿔야한다. [int multi](n1,n2) []를 지운다.
Function f = (n1, n2) {
n1 = 3; //
return n1 * n2;
};
//람다식 표현식
//n1 = 3; 이렇게 만들지 못한다.
//이유는 람다식은 한줄로 만들어야하기 때문에
Function f2 = (n1, n2) => n1 * n2;
void main() {
add(1, 2);
//add1("일", 2);
int result = minus(1, 2);
print(result);
print(f(1, 2));
}

코드설명
1. 함수 오버로딩
- 오버로딩:
- 여러 개의 함수가 같은 이름을 가지지만, 매개변수의 타입이나 수가 다른 경우를 말합니다.
- Java에서는 오버로딩을 지원하지만, Dart는 기본적으로 함수 오버로딩을 지원하지 않습니다.
- 대신, 매개변수의 기본값이나 옵셔널 매개변수를 사용하여 유사한 기능을 구현할 수 있습니다.
2. 타입 추론과 명시적 타입 선언
- 타입 추론:
add1
함수처럼 매개변수의 타입을 생략하면 Dart가 자동으로 타입을 추론합니다.- 이는 코드가 간결해지지만, 타입 안정성을 해칠 수 있습니다.
- 따라서, 가능한 경우 명시적으로 타입을 선언하는 것이 좋습니다.
- 명시적 타입 선언의 장점:
- 코드의 가독성이 높아집니다.
- 컴파일 시점에 타입 오류를 미리 잡을 수 있습니다.
- 협업 시 다른 개발자들이 코드를 이해하기 쉽습니다.
3. Function
타입과 익명 함수
Function
타입:- Dart에서 함수 자체를 변수로 취급할 수 있습니다.
Function
타입을 사용하면 다양한 형태의 함수를 변수에 할당할 수 있습니다.
- 익명 함수와 람다식:
- 익명 함수는 이름이 없는 함수로, 일회성으로 사용하거나 다른 함수의 인자로 전달할 때 유용합니다.
- 람다식은 익명 함수를 간결하게 표현한 형태로, 단일 표현식만 포함할 수 있습니다.
4. main
함수에서의 함수 호출
- 함수 호출:
add
,add1
,minus
,f
,f2
등 다양한 함수를 호출하여 결과를 확인할 수 있습니다.- 주석 처리된
add1("일", 2);
은 타입이 명시되지 않았기 때문에 실행 시 오류를 발생시킬 수 있습니다. 따라서 실제로는 주석을 유지하거나, 올바른 타입의 인수를 전달해야 합니다.
주의사항
minus
함수의 구현 오류:- 현재
return n1 = n2;
는n2
의 값을n1
에 할당하고 그 값을 반환합니다. 만약 의도한 것이 뺄셈이라면return n1 - n2;
로 수정해야 합니다. - 예시:
int minus(n1, n2) {
return n1 - n2;
}
add1
함수의 타입 문제:add1
함수는 타입이 명시되지 않아, 다양한 타입의 인수를 받을 수 있지만, 실제로는 타입이 일치하지 않을 경우 오류가 발생할 수 있습니다.- 따라서, 가능하면 타입을 명시적으로 지정하는 것이 좋습니다.
- 예시:
void add1(int n1, int n2) {
print(n1 + n2);
}
- 람다식과 다중 문장:
- 람다식은 한 줄로 표현해야 하므로, 다중 문장을 포함할 수 없습니다. 만약 여러 문장을 포함해야 한다면 익명 함수 블록을 사용해야 합니다.
- 예시:
- 타입을 명시적으로 선언하여 코드의 가독성과 안정성을 높이세요.
- 함수 오버로딩을 지원하지 않는 Dart의 특성을 이해하고, 대신 옵셔널 매개변수나 매개변수의 기본값을 활용하세요.
- 익명 함수와 람다식을 적절히 사용하여 코드의 간결함을 유지하세요.
- 함수 구현 시 논리 오류가 없는지 꼼꼼히 확인하세요.
Function f3 = (n1, n2) {
n1 = 3;
return n1 * n2;
};
결론
Dart에서 함수 선언, 타입 추론, 익명 함수, 람다식 등의 개념을 잘 보여주고 있습니다. 다음과 같은 점들을 유의하면서 코드를 작성하면 더욱 안전하고 효율적인 Dart 프로그램을 작성할 수 있습니다.
2. ex07.dart
//함수 행위를 결정하지 못할 때 ()안에 function 행위(리스너)를 넣는다.
//print 뽑는게 아니니까 이때는 익명함수를 쓰는게 좋다.
void whenComeMother(Function beh) {
beh();
}
void off() {
print("컴퓨터끄기");
}
//리턴 안 할 거면 whenComeMother() => print("컴퓨터끄기") 이런식은 쓰지 말자!
void main() {
whenComeMother(() {
print("컴퓨터 끄기");
});
}
//new 생성자 생략가능
// dog a = dog();

코드설명
고차 함수 (Higher-Order Function)
- 정의:
- 다른 함수를 인자로 받거나, 함수를 반환하는 함수를 고차 함수라고 합니다.
- 장점:
- 코드의 재사용성을 높이고, 유연한 동작을 구현할 수 있습니다.
- 콜백(callback) 패턴을 통해 비동기 작업이나 이벤트 핸들링에 유용하게 사용됩니다.
- 예제:
whenComeMother
는 인자로 받은 함수를 실행하는 고차 함수입니다. 이를 통해 다양한 동작을 유연하게 정의할 수 있습니다.
2. 익명 함수 (Anonymous Function)
- 정의:
- 이름이 없는 함수로, 주로 일회성으로 사용되거나 다른 함수의 인자로 전달될 때 사용됩니다.
- 장점:
- 코드의 간결성을 유지할 수 있습니다.
- 특정 작업을 간단하게 정의할 수 있어, 코드의 가독성을 높입니다.
- 예제:
main
함수에서whenComeMother
에 전달된() { print("컴퓨터 끄기"); }
는 익명 함수입니다.
3. 람다식 (Lambda Expression)
- 정의:
- 단일 표현식으로 함수를 간결하게 정의하는 방법입니다. Dart에서는 화살표 함수(
=>
)를 사용하여 람다식을 표현할 수 있습니다.
- 주의사항:
- 람다식은 단일 표현식만을 포함할 수 있으며, 여러 문장을 포함하려면 익명 함수를 사용해야 합니다.
- 예제:
- 주신 코드에서는 람다식을 사용하지 않고 익명 함수를 사용하여
whenComeMother
를 호출하고 있습니다. 이는 함수 내에서 여러 작업을 수행할 필요가 있을 때 적합합니다.
4. 함수 타입 지정
Function
타입:- Dart에서 함수 자체를 변수로 취급할 수 있으며,
Function
타입을 사용하여 함수를 인자로 받을 수 있습니다.
- 타입 명시의 중요성:
- 함수의 매개변수나 반환 타입을 명시적으로 지정하면, 코드의 안정성과 가독성이 향상됩니다.
- 예를 들어,
void whenComeMother(void Function() beh)
와 같이 구체적으로 함수의 시그니처를 지정할 수 있습니다. 이는 잘못된 타입의 함수가 전달되는 것을 방지할 수 있습니다. - 고차 함수를 활용하여 코드의 재사용성과 유연성을 높이세요.
- 익명 함수와 람다식을 적절히 사용하여 코드의 간결함을 유지하세요.
- 함수 타입을 명시적으로 지정하여 코드의 안정성과 가독성을 향상시키세요.
- 타입 안전성을 유지하기 위해 함수의 매개변수와 반환 타입을 구체적으로 정의하세요.
요약
Dart에서 고차 함수와 익명 함수를 활용하는 기본적인 예제입니다. 이를 통해 다른 함수의 동작을 유연하게 정의하고, 필요에 따라 다양한 행위를 전달할 수 있습니다. 다음과 같은 점들을 유의하면서 코드를 작성하면 더욱 안전하고 효율적인 Dart 프로그램을 작성할 수 있습니다.
3. ex08.dart
class Cat {
String name;
int age;
String color;
int thirsty;
//이게 쓸 일이 많다.
Cat(this.name, this.age, this.color, this.thirsty);
}
class Dog {
String name;
int age;
String color;
int thirsty;
//이니셜라이즈 키워드
//한줄 요약시
Dog(String name, int age, String color, int thirsty)
: this.name = name,
this.age = age,
this.color = color,
this.thirsty = thirsty;
}
void main() {
Dog("", 1, "", 1);
}
생성자(Constructor)의 두 가지 방식
- 생성자 파라미터 이니셜라이저 (Cat 클래스):
Cat(this.name, this.age, this.color, this.thirsty);
- 장점:
- 코드가 간결합니다.
- 클래스의 속성과 생성자 파라미터 간의 매핑이 명확합니다.
- 단점:
- 초기화 로직이 복잡할 경우 사용하기 어렵습니다.
- 이니셜라이저 리스트 (Dog 클래스):
dart코드 복사Dog(String name, int age, String color, int thirsty) : this.name = name, this.age = age, this.color = color, this.thirsty = thirsty;
- 장점:
- 초기화 로직을 자유롭게 작성할 수 있습니다.
- 상위 클래스의 생성자를 호출할 때 유용합니다.
- 단점:
- 코드가 다소 길어질 수 있습니다.
2. 생성자 오버로딩
- 설명:
- 오버로딩은 같은 이름의 함수를 여러 번 정의하여, 매개변수의 타입이나 수에 따라 다른 동작을 수행하게 하는 기법입니다.
- Java에서는 생성자 오버로딩을 지원하지만, Dart는 기본적으로 생성자 오버로딩을 지원하지 않습니다.
- 대안:
- 명명된 생성자: 같은 클래스 내에서 여러 생성자를 정의할 수 있습니다.
class Dog {
String name;
int age;
String color;
int thirsty;
Dog(this.name, this.age, this.color, this.thirsty);
// 명명된 생성자
Dog.brownDog(String name, int age)
: this(name, age, "갈색", 5);
}
void main() {
Dog dog1 = Dog("멍멍이", 3, "갈색", 7);
Dog dog2 = Dog.brownDog("브라운", 2);
}
class Dog {
String name;
int age;
String color;
int thirsty;
Dog(this.name, this.age, {this.color = "검은색", this.thirsty = 5});
}
void main() {
Dog dog1 = Dog("멍멍이", 3);
Dog dog2 = Dog("브라운", 2, color: "갈색", thirsty: 7);
}
요약
Dart에서 클래스와 생성자를 정의하는 두 가지 방식을 잘 보여주고 있습니다.
Cat
클래스는 생성자 파라미터 이니셜라이저를 사용하여 간결하게 속성을 초기화하고, Dog
클래스는 이니셜라이저 리스트를 사용하여 보다 유연하게 속성을 초기화하고 있습니다.또한, Dart에서는
new
키워드를 생략할 수 있으며, 생성자 오버로딩을 직접 지원하지 않지만 명명된 생성자나 옵셔널 매개변수를 통해 유사한 기능을 구현할 수 있습니다.- 생성자에서 타입을 명시적으로 지정하는 습관을 들이면 코드의 가독성과 안정성을 높일 수 있습니다.
- 명명된 생성자나 옵셔널 매개변수를 활용하여 클래스의 인스턴스를 다양한 방식으로 초기화할 수 있습니다.
4. ex09.dart
class Dog {
int age;
String name;
//오류가 사라지는 이유는 값이 무조건 들어가는 상황이기 때문에 오류가 나지 않는다.
//Dog(this.age, this.name)
//빌더 패턴이랑 비슷한 경우이다.
Dog({required this.age, required this.name});
}
//1. ?타입이 있고
//2. {} 선택적 매개변수를 받을 수 있다.
class Cat {
int? age;
String? name;
//값을 초기화 하고 싶은때는
//선택적 매개변수에게만 디폴트값을 설정 할 수 있다.
Cat({this.age, this.name = "토토"});
void cry() {
print("야옹");
}
//값을 바로 보고싶으면 오버라이딩을 해주면 된다.
//생성자 자동완성 키 찾아보기!
String toString() {
return "age: ${age}, name : ${name}";
}
}
//선택적 매개변수의 뜻은 넣어도 되고 안 넣어도 된다.
//age:1 이런식으로 생략이 가능하다. 순서도 바꿔도 상관없다.
//자바의 빌더패턴이 필요가 없다. 빌더패턴은 장점은 순서가 상관없다. 실수도 없다.
//required를 쓰면 name,age를 반드시 받아야한다.!!!!
//Cat의 경우는 하나만 써도 받을 수 있다.
void main() {
Dog d = Dog(name: "토토", age: 10);
Cat c = Cat(age: 10);
//케스케이드연산자
Cat c1 = Cat(age: 10)..cry();
print("-----------------------------------");
print(d);
print("-----------------------------------");
print(c);
print("-----------------------------------");
print(c1);
}

코드설명
1.명명된 매개변수(named parameters)와 필수 매개변수(required parameters)
- 명명된 매개변수(named parameters):
- 매개변수를 중괄호
{}
안에 정의하면, 객체를 생성할 때 매개변수의 이름을 명시적으로 지정하여 값을 전달할 수 있습니다. - 예:
Dog(name: "토토", age: 10);
- 장점:
- 매개변수의 순서에 구애받지 않습니다.
- 매개변수의 의미를 명확하게 전달할 수 있습니다.
- 필수 매개변수(required parameters):
required
키워드를 사용하면, 해당 매개변수를 반드시 제공해야 합니다.- 이는 객체 생성 시 필수적인 속성을 빠뜨리지 않도록 도와줍니다.
- 예:
Dog({required this.age, required this.name});
2. 널 허용 타입(null safety)
- Dart는 **널 안전성(null safety)**을 지원하여, 변수에
null
값을 허용할지 여부를 명확하게 지정할 수 있습니다.
- 널 허용 타입(nullable types):
int?
,String?
와 같이?
를 붙여서 선언하면, 해당 변수는null
값을 가질 수 있습니다.- 예:
int? age;
,String? name;
- 널 허용하지 않는 타입(non-nullable types):
int
,String
과 같이?
없이 선언하면, 해당 변수는null
값을 가질 수 없습니다.- 예:
int age;
,String name;
- 기본값 설정:
- 선택적 매개변수에 기본값을 설정하면, 값을 제공하지 않아도 기본값이 자동으로 할당됩니다.
- 예:
Cat({this.age, this.name = "토토"});
3. 오버라이딩(overriding)
toString
메소드 오버라이딩:toString
메소드를 오버라이딩하면, 객체를 문자열로 표현할 때 원하는 형식으로 출력할 수 있습니다.- 예:
@override
String toString() {
return "age: ${age}, name: ${name}";
}
4. 케스케이드 연산자(cascade operator)
- 정의:
- 케스케이드 연산자(
..
)는 객체를 생성한 후, 해당 객체의 메소드를 연속적으로 호출할 때 사용됩니다.
- 사용 예:
Cat c1 = Cat(age: 10)..cry();
c1
객체를 생성한 후, 바로 cry
메소드를 호출합니다.- 장점:
- 객체를 생성하면서 여러 메소드를 연속적으로 호출할 수 있어 코드가 간결해집니다.
- 주의사항:
- 메소드 체이닝을 사용할 때, 객체가 올바르게 초기화되었는지 확인해야 합니다.
5. 빌더 패턴과 Dart의 대안
- 빌더 패턴(builder pattern):
- Java 등에서 객체의 복잡한 생성 과정을 단순화하고, 매개변수의 순서에 구애받지 않도록 도와주는 디자인 패턴입니다.
- Dart의 대안:
- Dart에서는 명명된 매개변수(named parameters)와 필수 매개변수(required parameters)를 사용하여 빌더 패턴과 유사한 기능을 구현할 수 있습니다.
- 또한, 선택적 매개변수(optional parameters)와 기본값 설정을 통해 유연한 객체 생성을 지원합니다.
- 명명된 매개변수와 필수 매개변수를 적절히 사용하여 객체 생성을 명확하고 안전하게 관리하세요.
- 널 허용 타입을 통해 필요한 경우에만
null
을 허용하고, 그렇지 않은 경우에는 필수 값을 제공하도록 설계하세요. toString
메소드 오버라이딩을 활용하여 객체의 상태를 쉽게 출력하고, 디버깅 시 유용하게 사용하세요.- 케스케이드 연산자를 사용하여 객체 생성과 메소드 호출을 간결하게 표현하세요.
- 명명된 생성자나 빌더 패턴을 활용하여 복잡한 객체 생성 과정을 효율적으로 관리하세요.
요약
Dart에서 클래스와 생성자를 정의하는 다양한 방법을 잘 보여주고 있습니다. **명명된 매개변수(named parameters)**와 **필수 매개변수(required parameters)**를 사용하여 객체 생성 시 매개변수의 순서에 구애받지 않고, 필요한 값을 명확하게 제공할 수 있습니다. 또한, **널 허용 타입(null safety)**과 기본값 설정을 통해 더욱 유연하고 안전한 코드를 작성할 수 있습니다.
5. ex10.dart
// null 처리방법
int? findById(int id) {
return id == 1 ? 1 : null;
}
main() {
//위험한코드 1 :특정한 값이 들어오면 터질 수 있기때문에 "!" 확실하지 않을때 쓰면 안된다.
int r1 = findById(1)!;
print(r1);
print("-------------------------------------------------");
//위험한 코드 2: null 대체연산자(Orelse)
int r2 = findById(5) ?? 0;
print(r2);
print("-------------------------------------------------");
// findById(5).toDouble(); 이건 되지 않는다. null일 수 있기 때문에
//findById(5)!.toDouble();
//이 함수는 null이면 실행하지 말구 null 아니면 실행이 되게한다.
//이렇게 한 줄로 연산자를 쓸 수 있다.
double? r3 = findById(5)?.toDouble() ?? 0;
print(r3);
//null 이면 0.0으로 받는다.
//print(r3 ?? 0.0);
}

코드설명
findById
함수- 역할: 주어진
id
가1
이면1
을 반환하고, 그렇지 않으면null
을 반환합니다. - 반환 타입:
int?
는int
또는null
을 반환할 수 있음을 의미합니다.
main
함수 내 코드r1
선언 및 출력- 설명:
findById(1)
은1
을 반환하므로!
연산자를 사용해null
이 아님을 확신하고int
타입으로 변환합니다. - 주의: 만약
findById
가null
을 반환하면 런타임 오류가 발생합니다. - 출력:
1
r2
선언 및 출력- 설명:
findById(5)
는null
을 반환하므로,??
연산자를 사용해null
일 경우 기본값0
을 할당합니다. - 출력:
0
r3
선언 및 출력- 설명:
findById(5)
가null
이면?.
연산자로 인해toDouble()
이 호출되지 않고null
이 됩니다.??
연산자를 통해null
일 경우 기본값0
을 할당합니다.- 출력:
0
int r1 = findById(1)!;
print(r1);
int r2 = findById(5) ?? 0;
print(r2);
double? r3 = findById(5)?.toDouble() ?? 0;
print(r3);
핵심 포인트
- 널 단언 연산자(
!
): - 변수나 함수가
null
이 아님을 확신할 때 사용합니다. - 확신할 수 없을 경우 사용하면 런타임 오류가 발생할 수 있습니다.
- 널 병합 연산자(
??
): - 왼쪽 값이
null
일 경우 오른쪽 값을 반환합니다. - 안전하게 기본값을 제공할 때 유용합니다.
Share article