본문 바로가기

프로그래밍 언어 기초/Dart

[Dart] Dart 기본 문법1 (기본 출력과 자료형)

개요

오늘은 Flutter의 프로그래밍 언어 Dart의 문법에 대해서 알아보겠습니다.

프로그래밍 언어의 문법은 경험상 부연 설명 보다는 샘플 코드를 살펴보는 것이 더 효율적으므로

몇 개의 챕터로 나누어 기본 문법에 소개하겠습니다.

 

 

오늘의 포스팅에서 소개할 문법은 아래와 같습니다.

  • 출력문
  • 변수의 선언
  • dynamic
  • final과 const
  • dart의 자료형
  • dart의 컬렉션(Collections)
  • Enum

 

1. 출력문

DART의 출력문은 아래와 같습니다.

 

void main(){
    print('hello world!');
}

 


2. 변수의 선언

dart에서 변수의 선언은 var을 사용합니다. var는 타입 캐스팅을 지원하므로,

리터럴 값(1, false, "Hello World" 등)과 같은 실제 데이터가 입력되었을 때 그 자료형이 확정됩니다.

즉, 다른 프로그래밍 언어 등에서도 제공하는 '지연 초기화'가 가능합니다.

 

// fluter의 변수 선언

void main(){
    // 변수의 선언
    var name = 'hello world!';
    print(name); // hello world!

    // 변수의 재할당
    name = 'hello monkey!';
    print(name); // hello monkey!

    var hello;

    hello = 123;
    print(hello); // 123

}

 

 


3. dynamic

dart에서는 Java의 Objec, Kotlin의 Any 처럼 어떤 자료형이든 담을 수 있는 변수를 선언할 수 있습니다.

이는 자바스크립트의 변수(var, let 등)와도 유사한 문법으로 보입니다.

 

void main(){
    // dynamic을 통해 서로 다른 자료형 변수 담을 수 있음
    dynamic name = 'hello wolrd!';
    print(name); // hello wolrd!
    name = 1;
    print(name); // 1

}

 

 


4. final과 const

dart에서는 값이 변하지 않는 변수, 즉 상수 자료 형을 두 가지 지원합니다. ( final과 const )

이 두 자료형은 빌드 시점이나 런타임 중 검증하는 시점이 중요한 경우 용례가 달라질 수 있습니다.

즉, 빌드 시점에 상수를 확정해야 하는 경우 const를  사용해야하고, 런타임 시에 확정해야 하는 경우 final을 사용해야 합니다.

String과 같은 자료형은 어느 시점에서 검증하더라도 가능하므로 아래와 같이 작성할 수 있습니다.

반면에 DateTime과 같이 '시점'이 정해진 데이터의 경우에는 아래와 같이 구분하여 사용해야 합니다.

 

void main(){
    final String name = 'black monkey';
    // name = 'yellow monkey' // Error: Can't assign to the final variable 'name'.
    print(name); // black monkey

    const String name2 = 'pink banana';
    // name2 = 'yello banana'; // Error: Can't assign to the const variable 'name2'.
    print(name2); // pink banana

    // final -> 런타임 상수
    // const -> 빌드 타임 상수

    final DateTime now = DateTime.now();
    print(now); // ok

    // const DateTime now2 = DateTime.now(); // Error: Cannot invoke a non-'const' constructor where a const expression is expected. Try using a constructor or factory that is 'const'.
    // print(now2)

}

 


5. dart의 자료형

dart에서는 Dart는 모든 것이 객체이며, Dart에서는 원시형(primitive type)과 참조형(reference type) 데이터가 모두 객체로 취급됩니다. 그러나 개념적으로는 원시형과 참조형 데이터를 구분할 수 있습니다.

dart에서 개념적으로 구분되는  원시형(primitive type) 타입의 자료형은 아래와 같습니다.

 

void main(){

    String pet = '고양이';
    int age = 123;
    double avgAge = 12.3;
    bool isAdult = false;

    print(pet);
    print(age);
    print(avgAge);
    print(isAdult);
    isAdult = true; // re-allocate ok!
    print(isAdult);

    // var is for type late init!
    var myType = 123;
    print(myType);
    myType = 345; // ok
    print(myType);
    // myType = false; // Error: A value of type 'bool' can't be assigned to a variable of type 'int'.

}

 

또한, dynamic을 사용하지 아니한 모든 자료형의 경우 한번 자료형이 확정된 경우에는 다른 타입의 데이터를 넣을 수 없습니다.

 

💡 여기서 잠깐, dart에는 long과 float 자료형이 없나요?

dart에서 정수형에 해당하는 자료형은 int를 사용합니다. 그리고, 이 범위는 플랫폼이 몇 비트를 채택했는지에 따라 그 크기가 달라질 수 있습니다. 가령 32비트 체제에서는 대략 -2^31부터 2^31-1까지의 범위를 가집니다. 그러나, 64비트 체제에서는 대략 -2^63부터 2^63-1까지의 범위를 가집니다.

 

그럼에도 불구하고 이러한 범위를 넘어서는 정수형 자료형은 BigInt 라이브러리를 사용하여 처리할 수 있습니다.

 

또한, float과 같은 부동 소수점을 나타낼 수 있는 자료형의 경우도 지원하지 않습니다.

그러나, 이보다 큰 범위를 갖는 double을 사용할 수 있기 때문에, 64비트의 부동 소수점 수를 나타낼 수 있습니다.


6. dart의 컬렉션(Collections)

dart에서는 하나의 변수에 여러 개의 데이터를 담을 수 있는 자료형을 컬렉션이라고 정의합니다.

dart의 컬렉션은 상호간의 형변환이 자유로워 목적에 따라 다양한 자료형을 쉽게 변환 및 사용할 수 있습니다.

 

6.1. 리스트(List)

void main(){
    // collections -> List
    List<String> petList = ['Cat', 'Dog', 'Bird'];
    print(petList); // [Cat, Dog, Bird]
    // referece with index!
    print(petList[0]); // Cat
    print(petList[2]); // Bird
    print(petList.length); // list's length : 3

    petList[2] = 'Bee';
    print(petList[2]); // Bee

    // List's for-each
    petList.forEach((pet){
        print(pet);
    });

    // add
    petList.add("Lion"); // 쌍 따옴표도 가능
    print(petList); // [Cat, Dog, Bee, Lion]
    print(petList.length); // 4

    // add All
    var otherPetList = ["Tiger", "Bear", "Elephant"];
    petList.addAll(otherPetList);
    print(petList); // [Cat, Dog, Bee, Lion, Tiger, Bear, Elephant]
}

 

6.2. 컬렉션의 스트림(Stream) 메서드 where, map, reduce, fold

void main(){
    // collections -> List
    List<String> petList = ['Cat', 'Dog', 'Bird'];
    
    // where , 조건을 만족하는 데이터의 필터링
    final longAnimal = petList.where( // type is Iterable!
        (name) => name.length > 3 // 4글자 이상인 동물만!
    );
    print(longAnimal); // (Lion, Tiger, Bear, Elephant)
    // type cast to List?
    var longAniamlList = longAnimal.toList(); // Iterable → List
    print(longAniamlList); // [Lion, Tiger, Bear, Elephant]

    // map, 모든 값에 대해 동일한 '처리'를 해줄 때
    final headAinmals = petList.map((name)=> 'Pet $name');
    print(headAinmals); // (Pet Cat, Pet Dog, Pet Bee, Pet Lion, Pet Tiger, Pet Bear, Pet Elephant) type is Iterable

    // reduce, 람다 함수(익명 함수)의 전달, 컬렉션이 있는 특정 값을 하나로 합치는 연산 등
    // 단, reduce로 리턴하는 값은 컬렉션의 요소의 값과 타입이 일치해야함
    // 배열의 값이 int라면 int, String이라면 String 등으로 고정!
    final String allName = petList.reduce((value, element) => value + ', '+ element); // `value + ', ' + element'라는 형태의 익명 함수를 받는 형식, 결과적으로 전체 값을 더하는 연산을 수행하게 됨.
    // final int allLength = petList.reduce((value, element) => value + 0 + element); // Error: A value of type 'String' can't be assigned to a variable of type 'int'.\
    print(allName); // Cat, Dog, Bee, Lion, Tiger, Bear, Elephan

    // fold, reuce의 상위 호환 버전 느낌, 리턴 타입이 반드시 배열을 구성하는 요소와 자료형과 같지 않아도 됨!
    final int allLength = petList.fold<int>(0, (value, element) => value + element.length);
    print(allLength); // 30    
    
}

 

6.3. Map

void main(){
  

    // collections -> Map

    Map<String, String> books = {
        'Clean Code': 'Robert C. Martin', // key : value 형식으로 입력!
        'The Pragmatic Programmer': 'Andrew Hunt, David Thomas',
        'Effective Dart': 'Google',
        'Design Patterns': 'Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides',
        'Refactoring': 'Martin Fowler'
    };

    // 맵 출력
    print('First Book Author is ${books["Clean Code"]}'); // First Book Author is Robert C. Martin

    // Map's for-each
    books.forEach((title, author) {
        print('$title by $author');
    });

    // key 값 반환하기
    print(books.keys); // key 값 , type은 Interbale 이므로 toList() 메서드를 통해 타입 변환 가능!
    print(books.values); // value 값, type is Iterable

}

 

 

6.4. Set

void main(){
    // collections -> Set
    var petPool1 = { "Lion", "Tiger", "Bear", "Elephant", "Zebra" };
    Set<String> petPool2 = {  "Giraffe", "Rhinoceros", "Hippopotamus", "Cheetah", "Gorilla" };
    print(petPool1); // {Lion, Tiger, Bear, Elephant, Zebra}
    print(petPool2); // {Giraffe, Rhinoceros, Hippopotamus, Cheetah, Gorilla}

    // contains, 값이 있는지 확인
    print(petPool1.contains("Giraffe")); // false
    print(petPool1.contains("Lion")); // true
    print(petPool2.contains("Giraffe")); // true
    print(petPool2.contains("Lion")); // false

    var petPoolList1 = petPool1.toList(); // set -> List 변환 가능!
    print(petPoolList1); // [Lion, Tiger, Bear, Elephant, Zebra]

    // from(), List -> Set 변환
    var parsedSet = Set.from(petPoolList1);
    print(parsedSet); // {Lion, Tiger, Bear, Elephant, Zebra}
}

 


7. Enum

Enum, 즉 열거형은 상수(변경할 수 없는 변수) 그룹을 나타내는 특별한 "클래스"입니다.

어떤 분류에 속하는 데이터에서 유형을 정하는 경우, 예컨데 음료의 종류 또는 직업의 종류 등을 표현하는 등의 사례에서 활용될 수 있습니다.

dart에서 Enum은 아래와 같이 사용할 수 있습니다.

 

enum Drink {
  coffee,
  tea,
  juice,
  soda,
}

void main(){
    var drinkCoffee = Drink.coffee;
    print(drinkCoffee); // Drink.coffee
}