[TIL] Dart Variables, Operators, Collections, Conditions

Jul 30, 2024
[TIL] Dart Variables, Operators, Collections, Conditions

Variables

Keyword
Description
int
정수형
double
실수형
String
text
bool
true OR false
var
모든 type 저장 가능 BUT 초기화 후 type 변경 불가
dynamic
모든 type 저장 가능 AND 초기화 후 type 변경 가능

var VS. dynamic

var는 한 번 type이 선언되면 변경 불가하다
var name = 'Hyoeun'; print(name); // Hyoeun name = 24; // ERROR
dynamic은 type이 결정된 후에도 type을 변경할 수 있다
dynamic age = 24; print(age); // 24 age = '나이'; print(age); // 나이
Dart는 정적 타입 언어이므로 dynamic을 최대한 사용하지 않는 것이 좋다
🤔
아무래도 dynamic을 사용하면 코드를 읽을 때 헷갈리지 않을까 싶다!
var로 변수에 값을 할당하면 type을 auto casting한다
var a = 1; // 자동으로 int type으로 인식
var list = null; // var는 ?를 붙이지 않아도 기본적으로 nullable하다 var list2 = [0, ...?list]; // null이면 오류가 나지 않고 [0]을 출력한다 var listA = [2]; var listB = [...?listA, 22]; // ERROR: listA가 null이 아니기에 발생한 오류

초기화와 선언

초기화 = 변수에 처음으로 값을 할당하는 것
선언과 동시에 초기화하는 것이 좋다
void main() { int num; // 선언 num = 22; // 초기화 String name = 'Hyoeun'; // 선언과 동시에 초기화 }

null safety

Dart의 변수는 기본적으로 non-nullable 변수로 지정되어 있다
null도 하나의 값임을 인지하여야 한다
nullable 변수로 만들기 위해서는 type(자료형) 뒤에 ? 키워드를 붙여 선언하면 된다
int num; // non-nullable type int? num; // nullable type
! 키워드를 활용하면 강제로 non-nullable하게 만들 수 있다
String? name; name = stdin.readLineSync()!; // null이 아닌 값을 입력할 것이라 확신한다면 ! 키워드를 활용한다 int? num; num = 22; print(num!); // num에 절대로 null 값이 들어가지 않을 것이라 확신한다면 ! 키워드를 활용할 수 있다
! 키워드는 가급적 사용하지 않는 것이 좋다
🤔
? 키워드를 넣을지 말지 선언할 때 충분히 고민하는 것이 좋을 듯하다

Default value

Default value = 초기화 값
nullable 변수의 default value는 null이다
int? number; // 초기화 값 = null
non-nullable 변수는 초기화 값이 필수다
따라서 선언과 동시에 초기화하는 것이 일반적이다
  1. Top-level non-nullable 변수는 선언과 동시에 초기화한다
    1. top-level 변수: code block인 중괄호 { }에 속하지 않는 최상위 영역
      top-level 변수는 global 변수와 비슷한 개념이다
      String date = '2024년 7월 31일';
  1. Local non-nullable 변수는 선언과 동시에 초기화 되지 않더라도 변수 사용 전 반드시 초기화해야 한다
    1. void main() { int year; int month = stdin.readLineSync()!; if (1 <= month < 7) { int year = 2024; // 초기화 print('$year년 상반기입니다.'); } else { print('$year년 하반가입니다.'); } }

Late variables

나중에 초기화하기를 원할 경우 사용하는 키워드
개발자가 원할 때 초기화하고자 사용한다
개발자가 보장하는 방식은 개발의 편의성을 위해 존재하는 것이기 때문에 별로 좋지 않다
따라서 late도 사용하지 않는 것이 좋다

final과 const

final과 const는 변수를 고정시킨다 > immutable(변하지 않는) 변수
var처럼 final을 붙이면 auto casting되어 자동으로 type이 결정된다
final name = 'Hyoeun' // auto casting되어 자동으로 String type으로 인식 final int number = 1; // 가급적이면 연습을 위해 type 작성할 것 number = 2; // ERROR: 재할당 불가
const int age = 24; age = 22; // ERROR: 재할당 불가
final과 const의 차이
final: 런타임에 초기화되는 것을 허락한다
const: 컴파일타임에만 초기화될 수 있다
따라서 const는 final보다 조건이 좁기 때문에 더 빠르게 동작한다
영역이 좁을수록 속도가 더 빨라지므로 가능성을 줄여서 코딩하는 것이 좋다

console input

사용자로부터 입력을 받을 때 = stdin 활용
줄 바꿈 없이 출력할 때 = stdout 활용
stdin과 stdout은 dart:io package에 포함된 기능
따라서 import ‘dart:io’; 를 상단에 작성해 주어야 한다
readLineSync() : 입력을 받을 때까지 프로그램 실행을 멈추는 동기 입력
사용자가 아무것도 입력하지 않은 채 Enter를 누르면 null을 반환할 수 있으므로 readLineSync()! 처리한다

Operators

산술연산자
연산자
의미
비고
+
더하기
-
빼기
*
곱하기
/
나누기(몫)
실수형(double)로 변환
~/
나누기(몫)
정수형(int)로 변환
%
나누기(나머지)
정수형(int)로 변환
증감연산자
증감하는 타이밍이 연산자를 앞에 배치하느냐 뒤에 배치하느냐에 따라 다르다
  1. 전위증가
    1. ++a
      int a = 1; b = ++a; // a 값을 먼저 2로 증가 ⭢ b에 할당 즉 a = 2, b = 2
  1. 후위증가
    1. a++
      int a = 1; b = a++; // a 값을 b에 할당 ⭢ a 값 증가 즉 a = 2, b = 1
  1. 전위감소
    1. --a
      int a = 1; b = --a; // a 값을 먼저 0으로 감소 ⭢ b에 할당 즉 a = 0, b = 0
  1. 후위감소
    1. a--
      int a = 1; b = a--; // a 값을 b에 할당 ⭢ a 값 감소 즉 a = 0, b = 1
논리연산자
Operators
Description
&&
AND
||
OR
!
NOT

Collections

collection = 여러 개의 값을 그룹화하는 것 즉 데이터를 모아두는 것
순서
중복 가능 여부
List
O
O
Map
X
Key는 불가, Value는 가능
Set
X
X
Set은 List와 Map으로 구현 가능하기에 보통은 List와 Map을 많이 사용한다
List
순서 O / 중복값 허용 O
대괄호로 선언
String과 같이 사이즈 제한이 없다
index를 통해 value에 접근할 수 있다
List에 type을 지정하려면? List<String>
type을 따로 지정하지 않으면 dynamic으로 지정된다
generic type = 타입 제한 및 타입 명시
List 안에 List, Map을 중첩해서 넣을 수 있다 > nested list
List 자료를 합치려면 + 연산자 활용할 것
void main(List<String> arguments) { List firstMembers = ['Kim', 'Lee', 'Park']; List secondMembers = ['Jung', 'Shin']; print(firstMembers + secondMembers); // ['Kim', 'Lee', 'Park', 'Jung', 'Shin'] }
List 자료를 보통 item이라 부르지만 원래는 element가 맞다
List의 Method > 수없이 많지만 기본적인 것만 작성
testList.length // 리스트가 가진 아이템 개수 testList.contains(123) // 123이라는 argument가 있는지 확인 List<int> randomNum = [6, 5, 10, 52, 98]; randomNum.sort(); // 오름차순으로 정렬 print(randomNum); // [5, 6, 10, 52, 98]
spread operator(…): List 확장
List<int> number1 = [1, 2, 3, 4]; List<int> number2 = [5, 6, 7]; print([1, 2, 3, 4, ...numbers2]; // [1, 2, 3, 4, 5, 6, 7] print([...numbers1, ...numbers2]); // [1, 2, 3, 4, 5, 6, 7]
Map
순서 X / Key는 중복 허용 X, Value는 중복 허용 O
중괄호로 선언
key와 value 모두 dynamic으로 동작한다
따라서 list, map, object 전부 들어갈 수 있다
generic type으로 지정하려면 Map<String, dynamic> 형식으로 제한할 수 있다
다만 key와 value 중 하나만 타입 제한을 할 수는 없다
key는 거의 String을 사용한다
🤔
아무래도 key는 데이터의 의미를 설명하는 내용이 들어가니까 String을 많이 사용할 것 같다 Value는 실습을 진행하며 느낀 바로는 dynamic으로 명시하는 것이 좋았다 key에 무슨 내용을 담을지 모르는 상태라면🤭
void main() { Map<String, dynamic> Hyoeun = { // Map을 작성할 땐 중괄호를 사용한다! 'name': 'Hyoeun Lee', 'nickname': '1hyoeun3', 'age': 24, 'major': 'BA', }; print(Hyoeun['major']); // Map을 호출할 땐 대괄호를 사용한다! Hyoeun['school'] = 'Dongduk'; // 새로운 값 할당 Hyoeun.addAll({'city': 'Seoul'}); // 새로운 값 할당 Hyoeun.keys }
Set
순서 X / 중복값 허용 X
중괄호로 선언
데이터를 모아놓고 사용하는 collection > enum type과 유사
Set<String> members = {'D', 'A', 'C', 'B'}; print(members); var names = <String>{}; names.addAll(members); // 빈 Set에 members 요소 추가 names.add('E'); // E 값 추가 print(names.elementAt(1)); // A

Collection 추가

  1. 대괄호 [ ]는 List의 특성을, 중괄호 { }는 Map의 특성을 가졌다고 보면 된다
  1. iterable = 아이템에 순차적으로 접근 및 반복(loop) 가능한 자료구조

Conditions

condition = 조건문
if-else if-else
int score = 100; if (score >= 90) { print('A'); } else if (score >= 80) { print('B'); } else if (score >= 70) { print('C'); } else { print('F'); }
Switch-case
void main() { var int grade = 90; switch (grade) { case 90: case 80: case 70: // 동일한 값을 반환할 경우 이런 식으로 작성하면 된다 print('국가장학금 신청을 위한 최소 학점을 취득하였습니다.'); break; default: print('국가장학금 신청이 어려운 학점입니다.'); } }
🤭
사실 각기 다른 case인데 동일한 코드를 실행한다면 굳이 Switch문을 사용할 필요가 없는 것 아닐까 싶지만 알아두면 요긴하게 쓸 수도 있으니 적어보았다!
🧐
case에는 연산자를 사용할 수 없다! 말 그대로 케이스별로 용이하게 확인할 수 있고자 작성하는 코드이므로 필요하다면 if-else 사용할 것!
Switch expression
void main() { String engLastName = 'Lee'; String korLastName = switch (engLastName) { 'Kim' => '김', 'Lee' => '이', 'Park' => '박', _ => '흔하지 않은 성씨입니다.', // default 값은 underscore로 표현 }; print(korLastName); }
삼항연산자(conditional operator)
(조건식) ? trueValue : falseValue
삼항연산자를 그대로 번역하면 ternary operator인 까닭에 보통 ternary로 부르지만 삼항연산자가 하나 더 있는 Dart에선 conditional operator로 부르는 것이 적절하다
int number = 20; print( (number == 20) ? '20입니다.' : '20이 아닙니다.');

마치며

  1. parameter와 argument
    1. def plusFunction(a, b): // a와 b는 parameter return a + b plusFunction(10, 20) // 1020은 argument
  1. Map에 nested map 생성 시 대괄호를 사용하여 value 값을 출력할 수 있다
    1. Map<String, dynamic> student = { 'name': 'Hyoeun', 'id': '1hyoeun3', 'grades': {'math': 3.5, 'history': 4.0} }; print('history 과목 학점: ${student['grades']['history']}'); // 대괄호를 사용해 Key 값을 두 번 입력하면 nested map의 value 값이 출력된다
  1. Map에서 새로운 key와 value 값을 추가하려면 addAll method를 사용하면 된다
    1. Map<String, dynamic> country = { 'name': 'Indonesia', 'capital': 'Jakarta', 'population': 563254125 }; print('original: $country'); country.addAll({'currency': 'IDR'}); print('add currency: $country');
Share article

1hyoeun3