Dart 문법

플러터 사용 전 기본적으로 알아야하는 dart 문법 모음
화낼거양's avatar
Dec 20, 2024
Dart 문법
 
 

기본

 
// 1. dart는 모든 것들이 1급 객체이다. // 2. heap, stack, static //Heap: 동적 메모리 할당 영역. 객체가 여기에 저장 //Stack: 함수 호출과 관련된 지역 변수들이 저장되는 영역 //Static: 정적 메모리 영역. 클래스 변수 등이 여기에 저장 int n1 = 1; double d1 = 10.1; bool b1 = true; String s1 = "홍길동"; var n2 = 10; // 타입 추론 dynamic n3 = 20; // 오브젝트 타입 void main() { print("n1 ${n1.runtimeType}"); // 타입 확인 print("d1 ${d1}"); print("b1 ${b1}"); print("s1 ${s1}"); // n2 = 10.5; 생성시 int 형이 되기 때문에 double 형으로 바꿀 수 없다. n3 = 20.5; // 가능 }
 
  • 클래스 외부에 적게 되면 모두 static 영역에 띄워집니다.
  • var 타입은 모든 타입을 받을 수 있으며, 생성될 때 타입이 결정됩니다. 위의 예제에서는 int형으로 결정될 것이며 당연히 double 형으로 바꿀 수 없습니다.
 

Dynamic 타입 설명

 
dynamic 타입은 Dart에서 매우 유연하게 사용할 수 있는 데이터 타입으로, 변수의 타입을 런타임에 결정합니다. 다음은 dynamic 타입에 대한 상세 설명입니다:
 
  1. 모든 타입 할당 가능:
      • dynamic 타입은 숫자, 문자열, 객체 등 모든 타입을 할당할 수 있습니다. 이는 변수가 선언될 때부터 런타임까지 타입이 변경될 수 있음을 의미합니다.
  1. 런타임에 타입 결정:
      • dynamic 타입의 변수는 실행 도중에 할당되는 값에 따라 타입이 결정됩니다. 이는 컴파일 타임에 타입 검사가 이루어지지 않음을 의미합니다.
  1. 유연한 타입 변경:
      • dynamic 타입 변수는 다른 타입의 값을 할당할 수 있으며, 이는 코드의 유연성을 크게 높입니다. 예를 들어, 처음에는 int를 할당하고 나중에는 double이나 String을 할당할 수 있습니다.
  1. 타입 검사 및 경고 없음:
      • dynamic 타입을 사용할 때는 컴파일러가 타입 검사를 하지 않기 때문에 타입 오류가 발생할 가능성이 높아집니다. 따라서, 동적인 타입 할당을 필요로 하는 경우에만 신중하게 사용해야 합니다.
 
 
 

Null Safety란?

 
널 세이프티는 개발자가 널 에러를 피할 수 있도록 도와주는 다트 프로그래밍 언어의 기능입니다.
‘사운드 널 세이프티 인 다트’ 라고 불리며, 이를 통해 개발자는 코드 작성 시점에 널 에러를 잡을 수 있습니다.
 
💡
Sound Null Safety in dart : 런타임 중에 null 포인터 예외를 방지하기 위해 Dart 컴파일러가 코드를 분석하고 컴파일할 때 타입 시스템에서 엄격한 규칙을 적용하는 것을 의미
 
 
void main() { String name = "Jhon"; // 이 name이라는 변수는 null이 아닌 문자열만 가질 수 있다. int age = 30; // null 이 아닌 정수값만 가질 수 있다. String? nullableName; // 이 변수는 문자열 또는 null 값을 가질 수 있다. int? nullabelInt; // 이 변수는 정수값 또는 null을 가질 수 있다.nullabelInt // 방어적 코드 if (nullableName != null) { print("name : ${nullableName}"); } String? username = null; // null을 넣고 싶으면 물음표를 달아야한다. print(username); // null 출력 print(username == null ? "홍길동" : username); // 홍길동 출력 print(username ?? "홍길동"); // 홍길동 출력 }
 
  • null도 받고 싶다면 타입 뒤에 물음표를 추가하여야 하고, 기존의 일반적인 타입은 null을 받을 수 없습니다.
  • 마지막줄은 null 대체 연산자 입니다 (아직 정확한 이름이 안정해진 것으로 알고 있습니다).
    • 만약 username이 null이라면 “홍길동” 문자열을 출력합니다.
 
 

null 억제 연산자

void main() { String? name = "John"; // String? name = null; String nameNotNullable = name!; print("name : $name"); // John 출력 }
 
 

익명함수와 람다 표현식

// 익명 함수 Function k = () { print(1); return 1; }; Function h = () => 1; // 람다 표현식 : 1을 리턴하기 때문 void main() { int result = k(); print(result); }
 
  • 익명 함수:
    •  
      Function k = () { print(1); return 1; };
       
    • k는 숫자 1을 출력한 후 1을 반환하며 익명 함수라고 합니다.
 
  • 람다 표현식:
    •  
      Function h = () => 1; // 람다 표현식 : 1을 리턴하기 때문
       
    • h는 람다 표현식으로 작성된 함수로, 1을 반환합니다.
 
 
 

 
 

Another

 
 

생성자를 이용하여 초기화할 때

class Dog { String name; int age; String color; int thirsty;// 갈증 지수 Dog(this.name, this.age, this.color, this.thirsty); } void main() { Dog d1 = Dog("Toto", 13, "white", 100); Dog d2 = Dog("Mango", 2, "white", 50); print("d1의 이름은 ${d1.name}"); // Toto 출력 print("d2의 이름은 ${d2.name}"); // Mango 출력 }
 
  • 기존 자바와 비교하여 단순히 this.name, this.age 등으로 간단하게 작성할 수 있습니다.
 
 

선택적 매개변수

// 선택적 매개변수 class Person { // 기본값이 없기 때문에 물음표를 붙여야 함 String? name; // 기본값이 있기 때문에 물음표를 붙이지 않아도 가능 int money; // money는 매개 변수에 money 값을 전달하지 않을 시 기본값으로 0이 설정되어 있음 Person({this.name, this.money = 0}); } void main() { Person p1 = Person(name: "홍길동"); Person p2 = Person(name: "임꺽정", money: 10000); print("${p1.name}의 재산은 ${p1.money}"); // 홍길동의 재산은 0 출력 print("${p2.name}의 재산은 ${p2.money}"); // 임꺽정의 재산은 10000 출력 }
 
  • Dart 언어는 오버로딩이 없습니다. 대신 더 강력한 선택적 매개변수 방식을 사용합니다. 문법은 매개변수를 {}로 감싸면 됩니다.
 
 

cascade 연산자

// cascade 연산자 class Chef { String name; Chef(this.name); void cook() { print("요리를 시작합니다"); } } void main() { Chef c1 = Chef("홍길동")..cook(); // cascade 연산자 print("요리사 이름 ${c1.name}"); }
 
  • .. 연산자를 사용하면 코드 한 줄로 객체를 변수로 넘겨주면서 객체가 가진 함수를 호출할 수 있는 유용한 표기법입니다.
 
 

late

class Person { int id; String name; late int money; // 나중에 들어옴 or 디폴트 0 String? nickname; // 있을수도 있고 없을 수도 있는 것 Person(this.id, this.name, this.money, this.nickname); } void main() { Person p = Person(1, "name", 10, "nickname"); }
 
  • 변수의 초기화를 나중으로 미루고자 할 때 사용됩니다. 이는 주로 변수를 선언할 때 초기화할 수 없는 경우나, 변수를 초기화하기 위해 추가적인 논리가 필요한 경우에 유용합니다.
 
 

이름이 있는 생성자

class Person { int id; String name; late int money; // 나중에 들어옴 or 디폴트 0 String? nickname; // 있을수도 있고 없을 수도 있는 것 // 기본 생성자 Person(this.id, this.name, this.money, this.nickname); // 이름이 있는 생성자 Person.fromMap(Map<String, dynamic> m) : this.id = m["id"], this.name = m["name"], this.money = m["money"], this.nickname = m["nickname"] ?? "다른이름"; void init(){ if(money == 0){ for(int i=0; i<10; i++){ money = money + 1; } } } } void main(){ var m = { "id":2, "name":"임꺽정", "money":0 }; Person p2 = Person.fromMap(m)..init(); // cascade print(p2.id); print(p2.name); print(p2.money); print(p2.nickname); }
 
  • 생성자에 이름을 붙여 초기화 할 수도 있습니다.
  • 다만 ‘클래스이름.생성자이름’ 과 같은 방식으로 사용하기 때문에 기존 자바에서의 static 과 혼동할 수 있기 때문에 주의해야 합니다.
 
 

mixin

mixin class Engine { int power = 1000; } class Car with Engine { } void main() { Car c = Car(); print(c.power); // 1000 출력 }
 
mixin은 여러 클래스에 공통된 기능을 추가하는 데 사용됩니다. mixin을 사용하면 상속 없이 여러 클래스의 기능을 혼합할 수 있습니다. Dart에서 mixin은 클래스 간의 코드를 재사용할 수 있는 강력한 도구입니다. mixin을 사용하려면 with 키워드를 사용합니다.
 
  • mixin 키워드를 사용하여 클래스를 정의합니다.
  • with 키워드를 사용하여 Car 클래스에 Engine 믹스인을 추가합니다.
  • Car 클래스는 Engine 믹스인에 정의된 power 변수를 사용할 수 있습니다.
 
 

const

// const는 컴파일 시 초기화되며 final처럼 변경이 불가능하다 // 상수 데이터를 초기화할 때는 const를 사용한다 const String primaryColor = "green"; // final은 런타임 시 초기화 final String secondaryColor = "red"; // final과 const는 타입 생략이 가능 (타입 추론) // const primaryColor = "green"; // final secondaryColor = "red"; class Button { final String text; const Button(this.text); } void main() { // 동일한 버튼을 두 개 생성했으며 필드가 final이라 변경이 불가하고, // 이런 상황에서 const를 생성자에 붙여 쓰게 되면 Button b1 = const Button("로그인"); Button b2 = const Button("로그인"); // 동일한 내용일 때 새로 new 하는 것이 아닌 기존 것을 재사용한다. (상태를 변경할 수 없으며, 내용이 완전히 동일하기 때문) // 둘 다 동일한 해시코드 출력 print(b1.hashCode); print(b2.hashCode); }
 

const에 대한 설명:

 
  1. 컴파일 시 초기화:
      • const로 선언된 변수는 컴파일 시점에 초기화됩니다. 이는 해당 변수가 애플리케이션이 실행되기 전 컴파일 단계에서 이미 고정된 값을 가지게 됨을 의미합니다.
  1. 불변성:
      • const로 선언된 변수는 값을 변경할 수 없습니다. 이는 상수로서의 역할을 하며, 이후에 값을 재할당하는 것이 불가능합니다.
  1. 재사용성:
      • 동일한 const 값을 가진 객체를 생성할 때, 새로운 객체를 생성하지 않고 기존 객체를 재사용합니다. 이는 메모리를 절약하고, 객체 비교 시 효율성을 높입니다.
  1. const 생성자:
      • 클래스의 생성자에 const를 사용하면, 해당 클래스의 인스턴스가 불변 객체로 생성됩니다. 이는 클래스 인스턴스가 동일한 값을 가질 경우 동일한 객체를 재사용하게 만듭니다.
 
 

Collection

var list = [1,2,3]; // 원형 : List<dynamic> list = [1, 2, 3]; var map = { "id":1, "name":"홍길동" }; void main() { print(list[1]); // 2 출력 print(map["name"]); // 홍길동 출력 // 1. 값 추가 list.add(4); print(list); // [1, 2, 3, 4] 출력 // 2. map 값 추가 map["phone"] = "0102222"; print(map); // {id: 1, name: 홍길동, phone: 0102222} 출력 }
 
 
 

Collection 사용 기본 및 전개 연산자

var list = [1, 2, 3]; void main() { // 0. 깊은 복사 var r1 = [...list]; // 전개 연산자. ...list에 1, 2, 3 이 들어간다 print(r1); // [1, 2, 3] 출력 // 1. 추가 var r2 = [...list, 4]; // 전개 연산자. ...list에 1, 2, 3이 들어가고, 직접 적은 4 도 들어간다 print(r2); // [1, 2, 3, 4] 출력 // 2. 삭제 var r3 = list.where((e) => e != 2).toList(); print(r3); // [1, 3] 출력 // 3. 검색 var r4 = list.where((e) => e == 2).toList(); print(r4); // [2] 출력 // 4. 수정 var r5 = list.map((e) => e == 2 ? 5 : e).toList(); print(r5); // [1, 5, 3] 출력 }
 
  • 대괄호 안에 …list 와 같이 앞에 ‘점3개+list’ 를 적게 되면 list의 모든 원소들을 적용합니다. 이를 전개 연산자라고 합니다.
 
Share article

moohyun