Dart
Dart 공식문서
다트 ( Dart ) 함수
다트 ( Dart ) 연산자
Dart 의 함수
- 다트는 완전한 객체 지향 언어
- 다트에서는 모든 것이 객체이기 때문에 함수도 객체
- 함수가 객체이기 때문에 갖는 특징 존재
- 변수가 함수를 참조할 수 있음
- 함수의 인자로 함수를 전달할 수도 있음
- 선택 매개변수 ( Option Parameter ) 존재
- 선택 매개변수는 또 두가지로 나누어짐
- 이름 있는 선택 매개변수 ( Named Optional Parameter )
- 위치적 선택 매개변수 ( Positional Optional Parameter )
- 익명 함수 ( Anonymous Function ) 지원
- 람다식 ( Lambda Expression ) 지원
- 다트 함수의 특징 요약
- 변수가 함수 참조 가능
- 다른 함수의 인자로 함수 전달 가능
- 이름 있는 선택 매개변수
- 위치적 선택 매개변수
- 익명 함수 및 람다
변수가 함수 참조 가능
var name = getName() {}
var
타입 name 변수가 문자열을 리턴해주는 getName() 함수를 참조
- 그러면 name 은 문자열을 값으로 가지기 때문에
String
타입이 됨
- 흔하게 사용하는 함수 호출의 형태
# main.dart
main() {
int a = 10;
int b = 5;
var name = getName();
print("Name is $name");
print("$a + $b = ${add(a, b)}");
print("$a - $b = ${sub(a, b)}");
}
getName() {
retern "Kim";
}
int add(int a, int b) {
return a + b;
}
int sub(a, b) {
return a - b;
}
------------------------------------------------------------------------------------------
# 결과
Name is Kim
10 + 5 = 5
10 - 5 = 5
- 위 코드의 add() 함수를 보면 함수 리턴 타입과 매개변수 타입이 지정되어 있음
- 하지만 sub() 함수에서는 타입이 지정되지 않음
- 생략 가능하긴 하지만 가급적 명시하는 것이 좋음
다른 함수의 인자로 함수 전달 가능
getName(getFirstName(), getLastName()) {}
- 아래 코드를 보면 multi() 함수의 첫번째 매개변수에 add(a, b)를 두번째 매개변수에 sub(a, b)를 넘겨주었음
- 그러면 multi() 함수의 첫번째 인자는 add(a, b) 리턴값인 15가 되고 두번째 인자는 sub(a, b) 의 리턴값인 5가 됨
- 그리고 두 인자를 곱한 값이 multi() 함수의 리턴값이 됨
# main.dart
int add(int a, int b) {
return a + b;
}
int sub(a, b) {
return a - b;
}
int multi(int a, int b) {
return a * b;
}
main() {
int a = 10;
int b = 5;
print("${a + b} & ${a - b} = ${multi(add(a, b), sub(a, b)}");
}
------------------------------------------------------------------------------------------
# 결과
15 * 5 = 75
이름 있는 선택 매개변수
getAdress(String city, {String district, String zipCode="012345"})
- 함수 호출 시 매개변수에 인자값을 넘겨줄 때 매개변수명을 이용하여 인자값을 넘겨줄 수 있음
- 매개변수명으로 인자값을 넘겨줄 매개변수는
{}
로 감싸줘야 함
- 아래 코드는 기본 형태에서 약간 변형을 주었음
- district ∙ zipCode 를 선택 매개변수로 지정했고 zipCode 는 초기값을 가지도록 함
# main.dart
String getAddress(String city, {String district, String zipCode="012345"}) {
return "$city시 $district구 $zipCode";
}
main() {
print("${getAddress('서울', district: '강남')}");
print("${getAddress('서울', district: '강남', zipCode: "543210")}");
}
------------------------------------------------------------------------------------------
# 결과
서울시 강남구 012345
서울시 강남구 543210
- 첫번째 print() 함수처럼 zipCode 에 대한 인자값이 없으면 초기값을 사용
- 두번째 print() 함수처럼 새로운 인자값을 넘겨주면 그 값으로 할당
- 마지막 주석 처리된 세번째 print() 함수처럼 필수 매개변수와 혼용할 때는 기본 매개변수에 대한 인자값을 누락하면 안됨
위치적 선택 매개변수
getAdress(String city, [String district="강남", String zipCode="012345"]) {}
- 이름 있는 선택 매개변수 에서 zipCode 의 초기값을 지정한 형태를 살펴봤는데 그와 비슷한 개념
- 위치적 선택 매개변수는 미리 초기값을 정해놓고 함수 호출 시 해당 매개변수에 인자값을 넘겨주지 않으면 초기값을 사용
- 선언 방법은 선택 매개변수 지정을
{}
대신 []
로 하는 것이 차이점
# main.dart
String getAddress(String city, [String district="강남", String zipCode="012345"]) {
return "$city시 $district구 $zipCode";
}
main() {
print("${getAddress('서울')}");
print("${getAddress('서울', '서초')}");
}
------------------------------------------------------------------------------------------
# 결과
서울시 강남구 012345
서울시 서초구 012345
- 초기값을 변경하고 싶으면 새로운 인자값을 넘겨주면 됨
- 주의할 점은 이름 있는 선택 매개변수와 마찬가지로 필수 매개변수는 꼭 인자값을 줘야하고 매개변수 위치를 꼭 고려해야 함
- 예를 들어 위 코드의 getAddress('서울', '54321') 와 같이 호출하면 zipCode 가 변경되는 것이 아니라 district 가 변경됨
익명 함수 및 람다식
# 익명 함수
(a, b) { a + b; } ;
# 람다식
(a, b) => a - b;
- 익명 함수와 람다식의 차이점을 보면 결국
{};
를 =>
로 변경한 것
# main.dart
int add(int a, int b) {
return a + b;
}
var multi = (a, b) {
return a + b;
};
sub(a, b) => a - b;
main() {
int a = 10;
int b = 5;
print("$a + $b = ${add(a, b)}");
print("$a * $b = ${multi(a, b)}");
print("$a - $b = ${sub(a, b)}");
}
------------------------------------------------------------------------------------------
# 결과
10 + 5 = 15
10 * 5 = 15
10 - 5 = 5
Dart 의 연산자
- 다트는 다수의 전통적인 프로그래밍 언어에서 사용하는 기본 연산자를 동일하게 제공
- 추가적으로 모던 언어에서 차용한 연산자도 제공하고 있음
산술 연산자
- 거의 모든 프로그래밍에서 제공하는 기본적인 연산자
+
∙ -
∙ *
∙ /
∙ ~/
∙ %
∙ ++
∙ --
~/
: 정수를 결과값으로 가짐
%
연산자가 나머지를 구한다면 ~/
연산자는 몫을 구하는 연산자
# 예시
double divide(a, b) {
return a / b;
}
int divideQuotient(a, b) {
return a ~/ b;
}
int divideRemainder(a, b) {
return a % b;
}
main() {
int a = 11;
int b = 5;
print("$a / $b = ${divide(a, b)}");
print("$a ~/ $b = ${divideQuotient(a, b)}");
print("$a % $b = ${divideRemainder(a, b)}");
}
------------------------------------------------------------------------------------------
# 결과
11 / 5 = 2.2
11 ~/ 5 = 2
11 % 5 = 1
할당 연산자
=
∙ +=
∙ -=
∙ *=
∙ /=
∙ ~/=
등
+=
는 좌항 자신의 값에 우항의 값을 더한다는 의미
# 예시
main() {
int a = 10;
int b = 10;
a = a+ 5;
b += 5;
print("a = $a");
print("b = $b");
}
------------------------------------------------------------------------------------------
# 결과
a = 15
b = 15
관계 연산자 ( 비교 연산자 )
- 관계 연산자는 비교 연산자라고도 함
- 조건문에서 사용
==
∙ !=
∙ >
∙ <
∙ >=
∙ <=
# 예시
main() {
int a = 10;
int b = 20;
if (a == b) {
print("a == b");
} else {
print("a != b");
}
}
------------------------------------------------------------------------------------------
# 결과
a != b
비트 & 시프트 연산자
- 특정 로직을 수행할 때 더 빠른 속도를 낼 수 있으며 로우 레벨에서 레지스트리를 다룰 때 유용
&
( AND
) ∙ |
( OR
) ∙ ^
( XOR
) ∙ ~
( NOT
) ∙ <<
∙ >>
# 예시
main() {
int a = 0x03;
int b = 0x01;
print("a = $a");
print("a << 1 = ${a << 1}");
print("a >> 1 = ${a >> 1}");
print("a & 1 = ${a & 1}");
}
------------------------------------------------------------------------------------------
# 결과
a = 3
a << 1 = 6
a >> 1 = 1
a & 1 = 1
- 위의 코드는 비트 시프트와 AND 게이트를 다룸
- a 는 3 이므로 이진수로 0011 인데 이것을 왼쪽으로 1칸 비트 시프트 하면 0100 이 됨
- 반대로 0011 에서 오른쪽으로 1칸 시프트 하면 0001 이 되어 1 이 출력됨
$
( AND
) 연산은 두 게이트가 1 일 때만 1
- 0011 과 0001 에서는 마지막 비트만 동일하여 0001 이 됨
타입 검사 연산자
- 타입 검사 연산자는 2종류가 존재
as
: 형 변환
as
는 다른 타입으로 변환은 되지 않고 상위 타입으로 변환할 수 있음
is
: 객체가 특정 타입이면 true
is!
: 객체가 특정 타입이면 false (=특정 타입이 아니면 true)
is!
는 특정 타입이면 false 이며 특정 타입이 아니면 true
- 아래 코드는 Person 클래스를 상속한 Employee 클래스와 Student 클래스를 통해 형 변환 및 타입 검사를 하는 예제
# 예시
main() {
Employee employee = Employee();
Student student = Student();
Person person1 = employee as Person;
Person person2 = student as Person;
print("person1.name = ${person1.name}");
print("person2.name = ${person2.name}");
print("(employee as Person).name = ${(employee as Person).name}");
if (employee is Employee) {
print("employee is Employee");
} else {
print("employee is not Employee");
}
if (employee is Student) {
print("employee is Student");
} else {
print("employee is not Student");
}
if (employee is Person) {
print("employee is Person");
} else {
print("employee is not Person");
}
}
class Person {
var name = "person";
}
class Employee extends Person {
var name = "employee";
}
class Student extends Person {
var name = "student";
}
------------------------------------------------------------------------------------------
# 결과
person1.name = employee
person2.name = student
(employee as Person).name = employee
employee is Employee
employee is not Student
employee is Person
- employee 객체는 상위 타입인 Person 에도 속하기 때문에 true
조건 표현식
- 조건 표현식은 크게 3가지
- 삼항 연산자
- 조건적 멤버 접근 ( Conditional Member Access ) 연산자
??
연산자
(a > 0) ? '양수' : '음수';
if ( a > 0 ) {
return "양수";
} else {
return "음수";
}
- 조건적 멤버 접근 ( Conditional Member Access ) 연산자
- null 체크를 편하게 해줌
- 이 연산자는 좌항이 null 이면 null 을 리턴하고 아니면 우항의 값을 리턴
- 코틀린의 안전 호출 연산자와 형태와 의미가 동일
employee?.name
if ( employee == null ) {
return null;
} else {
return employee.name;
}
??
연산자
- 이 연산자는 null 체크 뿐만 아니라 null 일 경우에 대한 처리까지 가능
- 좌항이 null 이 아니면 좌항 값을 리턴하고 null 이면 우항 값을 리턴
- 코틀린의
?:
( 앨비스 연산자 ) 와 같음
employee.name ?? 'new name'
if ( employee.name != null ) {
return employee.name;
} else {
return 'new name';
}
# 예시
main() {
var employee;
print("employee name = ${(employee == null) ? null : employee.name}");
print("employee.name = ${employee?.name}");
employee = Employee();
print("employee name = ${(employee == null) ? null : employee.name}");
print("employee.name = ${employee?.name}");
print("employee.name = ${employee.name ?? 'assign employee name'}");
employee.name = null;
print("employee.name = ${employee.name ?? 'assign employee name'}");
}
class Employee {
var name = 'employee';
}
------------------------------------------------------------------------------------------
# 결과
employee name = null
employee.name = null
employee name = employee
employee.name = employee
employee.name = employee
employee.name = assign employee name
캐스케이드 표기법
- 캐스케이드 표기법 (
..
) 은 한 객체로 해당 객체의 속성이나 멤버 함수를 연속적으로 호출할 때 유용
- 매번 객체를 표기하고 호출하는 불필요한 과정을 줄여주기 때문
# 예시
main() {
Employee employee = Employee()
..name = "Kim"
..setAge(25)
..showInfo();
employee.name = "Park";
employee.setAge(30);
employee.showInfo();
}
class Employee {
var name = "employee";
int age = 20;
setAge(int age) {
this.age = age;
}
showInfo() {
print("$name is $age");
}
}
------------------------------------------------------------------------------------------
# 결과
Kim is 25
Park is 30
- 위 코드를 보면 employee 객체를 생성한 후 캐스케이드 표기법을 이용하여 해당 객체의 name ∙ setAge() ∙ showInfo() 에 연속적으로 접근하고 있음