kokoball의 devlog
article thumbnail
728x90

Flutter 개발을 시작하면서 내가 보려고 정리한 최소한의 Dart 문법 - 2

Null Safety

- null 값에 대한 안정성을 보장하기 위해 flutter 2.0, dart 2.12에 업데이트

- 기본값 초기화(initialize) 없이 선언하는 변수는 타입을 nullablenon-nullable 로 구분 필요

 

* nullable

- 일반 타입 뒤 물음표(?)를 추가하면 해당 변수는 해당 타입 or null 이 될 수 있음을 의미

- 초기화 없이 사용시 null 값 할당

void main() {
  String? name;
  int? age;
  bool? student;
  
  print(name)	//null
}

* non-nullable

- 물음표(?)가 없는 일반 타입은 null이 될 수 없는 값을 의미

- 사용전 초기값 할당 필요

 

* required

- 함수에서 named parameteroptional parameter를 설정할 때 non-nullable로 지정하기 위해 타입 앞에 붙임

void greeting({required String name}) {
  print('Hello $name!');
}

 

* non-nullable

- 클래스에서 non-nullable 프로퍼티가 필요하지만 디폴트 값이나 초기화를 시키지 않을 경우 나중에 값을 할당한다는 의미로 사용

class Cat {
  late String name;
  late int age;
}

 

함수의 구조

* 기본 구조

- 리턴값 타입 함수명 + (파라미터 타입 파라미터) {}

String test(String str) {
  return 'test function';
}

* 화살표 함수

- JS와 달리 화살표 함수도 함수 이름 필요

- { retrun 리턴값 } 을 => 리턴값으로 축약 가능

String test(String str) => 'test function';

* 람다 함수

- 이름이 없는 익명함수 (swift, kotlin 의 클로져에 해당)

var multiply = (int a, int b) => a * b;
print(multiply(5, 6));  // Prints: 30

- 리스트에서의 사용 예시

var numbers = [1, 2, 3, 4, 5];
numbers.forEach((number) => print(number));  // Prints: 1 2 3 4 5
  
var squares = numbers.map((number) => number * number).toList();
print(squares);  // Prints: [1, 4, 9, 16, 25]

- 정의부분과 실행부분을 분리하기 위해 람다함수를 파라미터로 넘겨주기도 함

void doSomething(Function callback) {
  callback();
}

// ....

doSomething(() => print('Hello from callback!'));  // Prints: Hello from callback!

Parameter

* named parameter

- 파라미터를 중괄호 ( {} ) 로 설정한 경우 이름으로 직접 파라미터를 전달 가능

void main() {
  void introduce({required String name, required int age}) {
    print('I am $name. $age yaers old!');
  }
  
  introduce(name: 'Sam', age: 5);	//I am Sam. 5 years old!
}

* optional parameter

- 파라미터를 대괄호 ( [] ) 로 설정한 경우 선택형 인자 설정 가능

void main() {
  void introduce(String name, int age, [String? food]) {
    print('I am $name. $age yaers old!');
    if (food is String) {
      print('I like $food!');
    }
  }

  introduce('Tom', 7);	//I am Tom. 7 yaers old!
  introduce('Sam', 5, 'ice cream');	//I am Sam. 5 yaers old! I like ice cream!
}

* default parameter

- = 연산자를 이용해 파라미터의 기본값 정의 가능

void main() {
  void introduce(String name, int age, [String food = 'chocolate']) {
    print('I am $name. $age yaers old! I like $food!');
  }

  introduce('Tom', 7); //I am Tom. 7 yaers old! I like chocolate!
}

객체지향 프로그래밍

- Dart는 객체지향 언어로서 클래스, 인스턴스, 상속, 추상클래스, 인터페이스, 믹스인 등을 지원

 

* 클래스, 인스턴스

class Person {
  String name; // Field

  // Constructor
  Person(this.name);

  // Method
  void greet() {
    print('Hello, I am $name');
  }
}

void main() {
  var john = Person('John'); // Instantiate the Person class
  john.greet(); // Prints: Hello, I am John
}

* 상속

- extends를 사용하여 상속받은 클래스를 정의 가능

class Employee extends Person {
  String department;

  // Constructor
  Employee(String name, this.department) : super(name);

  // Overridden Method
  @override
  void greet() {
    print('Hello, I am $name and I work in $department');
  }
}

void main() {
  var jane = Employee('Jane', 'HR'); // Instantiate the Employee class
  jane.greet(); // Prints: Hello, I am Jane and I work in HR
}

* 추상 클래스

- 다른 언어처럼 추상 메소드를 선언하여 상속으로 메소드 정의 강제 가능

abstract class Shape {
  double getArea(); // Abstract method

  void printArea() {
    print('The area is ${getArea()}');
  }
}

class Circle extends Shape {
  Circle(this.radius);

  final double radius;

  @override
  double getArea() => 3.14 * radius * radius; // Concrete implementation of abstract method
}

void main() {
  final circle = Circle(5);
  circle.printArea(); // Prints: The area is 78.5
}

* 인터페이스

- 다른 언어와 다르게 Dart에선 인터페이스도 class로 선언

- 인터페이스로 받을땐 클래스와 달리 implements로 사용

class Printable {
  void printValue() {}
}

class MyClass implements Printable {
  final int value;

  MyClass(this.value);

  @override
  void printValue() {
    print('The value is $value');
  }
}

void main() {
  final myClass = MyClass(10);
  myClass.printValue(); // Prints: The value is 10
}

* 믹스인

- class를 재사용하기 위한 문법 (가급적 상속을 피하기 위함)

- class를 상속받지 않고 클래스의 기능 이용 가능

mixin Flyable {
  void fly() {
    print('I can fly!');
  }
}

class Animal {
  void eat() {
    print('I can eat!');
  }
}

class Bird extends Animal with Flyable {
  void chirp() {
    print('I can chirp!');
  }
}

void main() {
  Bird bird = Bird();
  bird.eat();  // Prints: I can eat!
  bird.fly();  // Prints: I can fly!
  bird.chirp(); // Prints: I can chirp!
}

* 캡슐화

- 다른 언어와 다르게 private가 아닌 _를 prifix로 사용

class BankAccount {
  int _balance = 0; // private field

  int get balance => _balance; // getter

  void deposit(int amount) {
    if (amount > 0) {
      _balance += amount;
    }
  }
}

void main() {
  var account = BankAccount();
  account.deposit(100);
  print(account.balance); // Prints: 100
}

* 다형성

abstract class Animal {
  void makeNoise();
}

class Dog extends Animal {
  @override
  void makeNoise() {
    print('Woof!');
  }
}

class Cat extends Animal {
  @override
  void makeNoise() {
    print('Meow!');
  }
}

void main() {
  List<Animal> animals = [Dog(), Cat()];

  for (Animal animal in animals) {
    animal.makeNoise();
  }
}

* 예외 처리

void main() {
  try {
    int result = divideNumbers(10, 0);
    print('The result is $result');
  } catch (e) {
    print('An error occurred: $e');
  } finally {
    print('End of try-catch');
  }
}

int divideNumbers(int num1, int num2) {
  if (num2 == 0) {
    throw ArgumentError('You can\'t divide by zero!');
  }
  return num1 ~/ num2;
}

* 제네릭

// Generic Class Example
class Box<T> {
  T contents;
  Box(this.contents);
}

// Generic Function Example
T first<T>(List<T> items) {
  return items[0];
}

void main() {
  var box1 = Box<int>(10);
  print(box1.contents);

  var box2 = Box<String>('Hello');
  print(box2.contents);

  var numbers = [1, 2, 3];
  print(first(numbers));

  var words = ['apple', 'banana', 'cherry'];
  print(first(words));
}

 

 

728x90
profile

kokoball의 devlog

@kokoball-dev

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!