12 분 소요

1. 함수와 제어문

1) 함수 선언과 호출하기

(1) 함수 선언 위치

  • 톱 / 클래스 / 함수 내부에서 함수 선언 가능
      void some1() {
          void some2() {}
      }
    	
      class Myclass {
          void some3() {}
      }
    
  • 오버로딩 미지원
    • 오버로딩 : 동명 함수를 타입, 개수를 다양하게 여러개 만드는 방법
        class My Class {
        void some() {}
        void some(int a) {}  // 오류(함수 이름 중복)
        }
      

(2) 매개변수 타입

  • 타입 지정
      void some(int? a) {}
    	
      main() {
          some(20);
          some(null);
          some('hello'); // 오류
      }
    
  • 타입 미지정(var 선언, 형식 생략) → dynamic 타입
      void some(var a) {}  // var 선언 
      // void some(a) {}  // 생략
    	
      main() {
          some();  // 오류
          some(15);
          some('world');
      }
    

(3) 함수의 반환 타입

  • 반환 타입 지정(없을 시 void, return 미지정 시 null)
      void some1() {}  // 없음
      int some2() {return 20;}  // int 20
      dynamic some3() {return 20;}  // dynamic 20
      some4() {}  // dynamic null
    

(4) 화살표 함수

void some() {
print('hello world');
}

void some() => print('hello world');

2) 명명된 매개변수

  • 일반 함수 호출 시, 매개변수의 개수/타입/순서에 맞게 데이터 전달
      void some(int a, String b, bool c) {}
    
      main() {
          some();  // 오류
          some('hello', true, 10);  // 오류
          some(10, 'hello', true);
      }
    

(1) 명명된 매개변수

  • 옵셔널(Optional) : 함수의 매개변수를 선택적으로 지정 하는 기능
  • 이름:값 형태
      void some({String? data}) => print('data: $data');  
    	  
      main() {  
      some(data: 'world');  
      }
    

(2) 명명된 매개변수 선언 규칙

  • 중괄호 {}로 묶어, 마지막에 한번만 선언
  • 기본값 설정 가능
      void some1({String? data1, bool? data2}, int data3) { } // 오류
      void some2(int data1, {String? data2, bool? data3}, {int? data4}) { } // 오류
      void some3(int data1, {String? data2, bool? data3}) { } // 성공
    

(3) 명명된 매개변수 호출 규칙

  • 데이터 전달 순서 상관 없이 전달 시 이름 명시
  • 모든 매개변수 전달 필요X(기본값 활용)
      void some(int data1, {String? data2, bool? data3}) {}
    	
      main() {
      some();  // 오류
      some(10);  // 성공
      some(10, 'hello', true);  // 오류
      some(data3:true, 10, data2:'hello');  // 성공
      }
    

(4) 기본 인자 설정 : 기본값

String myFun({String data = 'hello'}){
	return data;
}

main() {
	print('myFun() result : ${myFun()}');  // myFun() result : hello
	print('myFun(world) result : ${myFun(data:"world")}');  // myFun() result : hello
}

(5) 필수 매개변수 선언 - required

some({required int arg}) {print(arg);}

main() {
	some();  // 오류
	some(arg:10);  // 성공
}

3) 옵셔널 위치 매개변수(optional positional parameter)

  • 대괄호 []로 묶어 마지막에 한번만 선언
  • 기본값 설정 가능
      void some(int arg1, [String arg2 = 'hello', bool arg3 = false]) { }
    	
      main() {
          some();  // 오류
          some(10);  // 성공
          some(10, arg2:'world', arg3: true);  // 오류
          some(10, 'world', true);  // 성공
          some(10, 'world');  // 성공
      }
    

4) 함수 타입 인수

  • 함수타입(function type) : 함수를 대입할 수 있는 객체
      void some() { }
      Function data = some;
    
      int plus(int no) =>	no + 10;
      int multiply(int no) => no * 10;
    	
      Function testFun(Function argFun) {  
          print('argFun : ${argFun(20)}');
          return multiply;
      }
    	
      main(List<String> args) {
          var result = testFun(plus);  // argFun : 30
          print('result : ${result(20)}');   // result : 200
      }
    
      some(int fun(int a)) {print(fun(30));}  
    	  
      main() {
      some((int a) {return a + 20;});   // 50
      }
    

(1) 익명 함수

  • 정의 : 이름이 생략된 함수
  • 익명 함수(annonymous functions) == 람다 함수(lambda function)
      Function fun = (arg) {return 10;}  //함수 이름이 없다(fun이라는 객체에 담은 것)
    
      var list = ['apples', 'bananas', 'oranges'];  
    	  
      main() {  
      list.forEach((item) {  
      print('${list.indexOf(item)}: $item');  // 0: apples,...
      });  
      }
    

5) 게터와 세터 함수

  • 게터(getter) : get예약어(데이터 가져오기)
  • 세터(setter) : set예약어(데이터 변경하기)
      String _name = 'Hello';  
      String get name {  
      return _name.toUpperCase();  
      }  // 데이터 가져오기
    	  
      set name(value) {  
      _name = value;  
      }  // 데이터 변경하기(매개변수 필요)
    	  
      main() {  
      name = "World";   // get, set이 선언되어있기에 가능
      print('name : $name');  
      }
    

6) 기타 연산자 알아보기

(1) 나누기 연산자 - /, ~/

main() {
	int a = 8;
	print('a / 5 = ${a / 5}');  // 1.6
	print('a ~/ 5 = ${a ~/ 5}');  // 1
}

(2) 타입 확인과 변환 - is, as

class User {
	void some() {
		print("Hello");
	}
}

main() {
	Object obj = User();
	// obj.some();   // 오류

	if (obj is User) {   // 타입 확인, 자동 형 변환
		obj.some()
		}

	Object obj1 = User();
	(obj1 as User).some();  // 명시적 형 변환
}
  • 다트의 최상위 클래스 : Object(>User)
  • 객체를 변수 대입 시, 하위→상위(형 변환)은 자동
  • 상위→하위(형 변환)은 is(자동 형 변환), as(명시적 형 변환)을 실시해야함

(3) 반복해서 접근하기 - .. / ?..

class User{
	String? name;
	int? age;

	some() {
		print('name: $name, age: $age');
	}
}
  • 객체 생성과 멤버 접근
      var user = User();
      user.name = 'kkang';
      user.age = 10;
      user.some();  // name: kkang, age: 10
    
  • 캐스케이드 연산자 사용 예(마지막에만 ;)
    • 클래스가 일반 객체일 시 .. / Nullable객체일 시 ?..
        User()
        ..name = 'kkang'
        ..age = 30
        ..some();
      

7) 실행 흐름 제어하기

(1) for 반복문에서 in 연산자

  • 작성법 : 초기화; 조건; 증감
      main() {
          var list = [10, 20, 30];
          for (var i = 0; i < list.length; i++) {
              print(list[i]);  // 10  20  30
              }
      }
    
  • in 연산자로 간소화
      main() {
          var list = [10, 20, 30];
          for (var x in list) {
              print(x);
              }
      }
    

(2) switch~case 선택문

  • 실행 흐름을 분기할 때 사용 / 그다음 실행흐름 결정
    • break : switch문 나가기
    • continue : 특정 위치(라벨)로 이동하기
    • return : swith문 종료하기(반환하기)
    • throw : switch문 종료하기(던지기)
        some(arg) {
        swith(arg) {
            case 'A':
                print('A');  // 오류[break, continue, return, throw을 지정X]
            case 'B':
                print('B');
        }
        }
      

(3) 예외 던지기와 예외 처리

  • ✨예외 처리문 : 런 타임 오류를 제어
  • 예외를 던지는 throw 문
    • 예외 던지기
        some() {
            throw Exception('my exception');
        }
      
    • 문자열 던지기
        some() {
            throw 'my exception';
        }
      
    • 사용자 정의 객체 던지기
        some() {
            throw User();
        }
      
  • try~on~finally 예외 처리
      some() {
          throw FormatException('my exception');
      }
    	
      main(List<String> args) {
          try {
              print('step1....');   // step1....
              some();
              print('step2....');
          } on FormatException {
              print('step3....');   // step3....
          } on Exception {
              print('step4....');
          } finally {
              print('step5....');   // step5....
          }
          print('step6....');   // step6....
      }
    
    • 예외 객체 가져오기
        some() {
            throw FormatException('my exception');
        }
      		
        main(List<String> args) {
            try {
                print('step1....');   // step1....
                some();
                print('step2....');
            } on FormatException catch(e) {
                print('step3....$e');   // step3....FormatException: my exception
            } on Exception catch(e) {
                print('step4....$e');
            } finally {
                print('step5....');   // step5....
            }
            print('step6....');   // step6....
        }
      
    • try~catch 예외 처리(예외 종류 미구분)
        try {
            some();
        } catch(e) {
            print('catch....$e')
        }
      

2. 클래스(객체, 생성자, 상속, 추상)

1) 클래스와 객체

(1) 클래스 선언과 생성

  • 클래스 선언
      class User {
          String name = 'kkang';
          int age = 10;
    	
          void sayHello() {
              print('Hello $name, age : $age');
          }
      }
    
  • 객체 생성
      User user1 = new User();  // new는 생략가능
    

(2) 객체 맴버와 클래스 멤버

  • 멤버 : 클래스 내부에서 선언된 변수나 함수
    • 객체 멤버 : 단순하게 선언한 멤버(객체 생성 후 접근 가능)
    • 클래스 멤버 : static예약어로 선언한 멤버(클래스 명칭으로 접근 가능)
        class MyClass {
        String data1 = 'hello';  // 객체 멤버
        static String data2 = 'hello';  // 클래스 멤버
      	
        myFun1() {
            print('myFun1 call....');
        }
        static myFun2() {
            print('myFun2 call....');
        }
        }
      
    • 객체 멤버 이용
        MyClass.data1 = 'world';  // 오류
        MyClass obj = MyClass();
        obj.data1 = 'world';  // 성공
      
    • 클래스 멤버 이용
        MyClass.data2 = 'world';  // 성공
        MyClass obj = MyClass();
        obj.data2 = 'world';    // 오류
      

2) 생성자와 멤버 초기화

(1) 생성자 선언

  • 생성자(constructor) : 객체를 생성할 때 호출되는 함수
    • 모든 클래스가 소유(만들지 않더라도 자동으로 같은 이름의 기본 생성자를 만듬)
        class User {
        User() { }
        }
      

(2) 멤버 초기화하기

  • 멤버 초기화 생성자
      class User {
          late String name;
          late int age;
          User(String name, int age) {
              this.name = name;
              this.age = age;
          }
      }
    
  • 멤버 초기화 생성자 단순화
      class User {
          late String name;
          late int age;
          User(this.name, this.age);
    	
          sayHello() {
              print('name : $name, age : $age');
          }
      }
    

(3) 초기화 목록

  • 생성자 선언 시 초기화 목록(initializer list)을 사용 가능
  • 리스트의 데이터로 초기화
      class User {
          late int data1;
          late int data2;
    	
          MyClass(List<int> args)
              : this.data1 = args[0],
                this.data2 = args[1] { }
      }
    
  • 클래스 멤버 함수의 반환 값으로 초기화
      class MyClass {
          late int data1;
          late int data2;
    	
          MyClass(int arg1, int arg2)
              : this.data1 = calFun(arg1),
                this.data2 = calFun(arg2) { }
    	
          static int calFun(int arg) {
              return arg * 10;
          }
    	
          printData() {
              print('$data1, $data2');
          }
      }
    

3) 명명된 생성자

  • 정의 : 다른 이름으로 여러 생성자를 정의하는 기법(자바의 오버로딩)
  • 명명된 생성자 선언
      class MyClass {
          MyClass() { }
          MyClass.first() { }
          MyClass.second() { }
      }
    
  • 명명된 생성자로 객체 생성
      var obj1 = MyClass();
      var obj2 = MyClass.first();
      var obj3 = MyClass.second();
    

(1) this로 다른 생성자 호출하기

  • 예시 : MyClass.first호출 시, MyClass도 호출하고 싶을 때 사용
  • this() 잘못된 호출 예
      class MyClass {
          MyClass(int data1, int data2) {
              print('MyClass() call....');
          }
    
          // 1. this()는 {} 안쪽에 쓸 수 없다
          MyClass.first(int arg) {
              this(arg, 0);  // 오류
          }
    
          // 2. this() 사용 시 {}을 작성할 수 없다.
          MyClass.first(int arg) : this(arg, 0) { } // 오류
    	
          // 3. this() 사용 시 해당 호출문 외에 다른 구문 사용할 수 없다.
          MyClass.first(int arg) : this(arg, 0), this.data1=arg1; // 오류
      }
    
  • this() 올바른 호출 예
      class MyClass {
          MyClass(int data1, data2) {
              print('MyClass() call....');
          }
          MyClass.first(int arg) : this(arg, 0); // 성공
      }
    
  • ✨ 명명된 생성자 중첩 호출
    class MyClass {
      late int data1;
      late int data2;
      MyClass(this.data1, this.data2);
      MyClass.first(int arg) : this(arg, 0); // 기본 생성자(MyClass) 호출
      MyClass.second() : this.first(0); // 명명된 생성자(MyClass.first) 호출
    }
    

4) 팩토리 생성자

  • 용도 : 상황에 맞는 객체를 준비해 반환할 때(캐시 알고리즘이나 상속 관계에 따른 다형성을 구현할 때 유용)
    • 팩토리 생성자(factory constructor)는 factory 예약어로 선언
    • 팩토리 생성자는 반드시 객체를 반환해주어야 함.✨
  • 구현 예시
      class MyClass {
          MyClass._instance();
          factory MyClass() {
              return MyClass._instance();
          }
      }
      main() {
          var obj = MyClass();
      }
    
  • 캐시(cache) 알고리즘 구현 예시
    class Image {
      late String url;
      static Map<String, Image> _cache = <String, Image>{};
      Image._instance(this.url);
      factory Image(String url) {
          if (_cache[url] == null) {
              var obj = Image._instance(url);
              _cache[url] = obj;
          }
          return _cache[url]!;
      }
    }
    main() {
      var image1 = Image('a.jpg');
      var image2 = Image('a.jpg');
      print('image1 == image2 : ${image1 == image2}');
    }
    

5) 상수 생성자

(1) const로 생성자 선언

  • 상수 생성자 선언
      class MyClass {
          const MyClass();
      }
    
  • 상수 생성자 잘못 선언한 예
      class MyClass {
          int data1;
          const MyClass();  // 오류
      }
    
  • 상수 생성자의 객체 생성
      class MyClass {
          final int data1;
          const MyClass(this.data1);
      }
    
      main() {
          var obj1 = MyClass(10);
          var obj2 = MyClass(20);
          print('obj1.data : ${obj1.data1}, obj2.data : ${obj2.data1}');
      }
    

(2) const로 객체 생성

  • 상수 객체 생성 오류
      class MyClass { }
    	
      main() {
          var obj1 = const MyClass();  // 오류
      }
    
  • 상수 객체 생성
      class MyClass {
          final int dat1;
          const MyClass(this.data1);
      }
    	
      main () {
          var obj1 = const MyClass(10);
      }
    
  • 일반 객체 선언(같은 값으로 초기화했지만 서로 다른 객체이므로)
      var obj1 = MyClass(10);
      var obj2 = MyClass(10);
      print('obj1 == obj2 : ${obj1 == obj2}');  // false
    
  • 같은 값으로 상수 객체 선언(obj1 객체가 있으므로 obj2에 대입함)
      var obj1 = const MyClass(10);
      var obj2 = const MyClass(10);
      print('obj1 == obj2 : ${obj1 == obj2}');  // true
    
  • 다른 값으로 상수 객체 선언
      var obj1 = const MyClass(10);
      var obj2 = const MyClass(20);
      print('obj1 == obj2 : ${obj1 == obj2}');  // false
    
  • 같은 값으로 상수 객체와 일반 객체 선언
      var obj1 = const MyClass(10);
      var obj2 = MyClass(10);
      print('obj1 == obj2 : ${obj1 == obj2}');  // false
    

6) 상속(inheritance)

(1) 상속과 오버라이딩

  • 상속(inheritance) : 클래스를 재활용하는 객체지향 프로그래밍의 핵심
    • 기존 클래스 : 부모 클래스
    • 상속받은 새 클래스 : 자식 클래스
    • 예약어 : extends
    • 오바리이딩(overriding) : 상속 받은 멤버 재정의
  • 함수에서 널 불허 지역 변수 초기화
      class SuperClass {
          int myData = 10;
          void myFun() {
              print('Super..myFun()...');
          }
      }
    	
      class SubClass extends SuperClass {
      }
    	
      main(List<String> args) {
          var obj = SubClass();
          obj.myFun();  // 'Super..myFun()...'
          print('obj.data : ${obj.myData}');  // obj.data : 10
      }
    
  • 오버라이딩
      class SuperClass {
          int myData = 10;
          void myFun() {
              print('Super..myFun()...');
          }
      }
    	
      class SubClass extends SuperClass {
          int myData = 20;
          void myFun() {
              print('Sub... myFun()...');
          }
      }
    	
      main(List<String> args) {
          var obj = SubClass();
          obj.myFun();  // 'Sub..myFun()...'
          print('obj.data : ${obj.myData}');  // obj.data : 20
      }
    
  • 부모 클래스의 멤버에 접근하기
      class SuperClass {
          int myData = 10;
          void myFun() {
              print('Super..myFun()...');
          }
      }
      class SubClass extends SuperClass {
          int myData = 20;
    	
          void myFun() {
              super.myFun();   // Super..myFun()...
              print('Sub... myFun()..myData : $myData, super.myData : ${super.myData}');   // Sub... myFun()..myData : 20, super.myData : 10
          }
      }
    	
      main(List<String> args) {
          var obj = SubClass();
          obj.myFun();
      }
    

(2) 부모 생성자 호출하기

  • 자식 클래스의 생성자 호출(부모 생성자는 자동 호출됨)
      class SuperClass {
          SuperClass() {}
      }
      class SubClass extends SuperClass{
          SubClass() {}
      }
      main() {
          var obj = SubClass();
      }
    
  • 부모 생성자 호출
      class SuperClass {
          SuperClass() {}
      }
      class SubClass extends SuperClass {
          SubClass() : super() {}
      }
    
  • 부모 생성자가 매개변수나 명명된 생성자를 가진다면 생략하면 안됨
      class SuperClass {
          SuperClass(int arg) {}
          SuperClass.first() {}
      }
    	
      class SubClass extends SuperClass {
          SubClass() : super(10) {}   // 성공(생략가능)
          SubClass.name() : super.first() {}  // 성공(생략불가)
      }
    

(3) 부모 클래스 초기화

  • 부모 클래스의 멤버 변수 초기화1
      class SuperClass {  
          String name;  
          int age;  
          SuperClass(this.name, this.age) {}  
      }  
      class SubClass extends SuperClass {  
          SubClass(String name, int age) : super(name, age) {} // 부모 클래스 멤버 초기화
      }  
      main() {  
          var obj = SubClass('kkang', 10);  
          print('${obj.name}, ${obj.age}');  // kkang, 10
      }
    
  • 부모 클래스의 멤버 변수 초기화2
      class SuperClass {  
          String name;  
          int age;  
          SuperClass(this.name, this.age) {}  
      }  
      class SubClass extends SuperClass {  
          SubClass(super.name, super.age) {} // 부모 클래스 멤버 초기화
      }  
      main() {  
          var obj = SubClass('kkang', 10);  
          print('${obj.name}, ${obj.age}');  // kkang, 10
      }
    

7) 추상 클래스와 인터페이스

(1) 추상 클래스

  • 추상 클래스 : 추상 함수만 제공하여 상속받는 클래스에서 반드시 재정의해서 사용하도록 강제하는 방법(abstract를 통해 선언)
      abstract class User{
          void some();  // 성공
      }
      class Customer extends User {
          @override
          void some() {}  // 오버라이드 하는 곳
      }
    

(2) 인터페이스 알아보기

  • 인터페이스(interface) : 부모의 특징을 도구로 사용해 새로운 특징을 만들어 사용하는 방법
  • 인터페이스는 모든 멤버 재정의 해야 함!
      class User {
          int no;
          String name;
          User(this.no, this.name);
          String greet(String who) => 'Hello, $who. I am $name. no is $no';
      }
    	
      class MyClass implements User {
          int no = 10;
          String name = 'kim';
          @override
          String greet(String who) {
              return 'hello $who';
          }
      }
    
  • 인터페이스 타입 객체 선언
      main(){
          User user = MyClass();
          print('${user.greet('lee')}');
      }
    
  • 한 클래스에 여러 인터페이스 지정
      class MyClass implements User, MyInterface {}
    

8) 멤버를 공유하는 믹스인

(1) 믹스인 선언

  • mixin라는 예약어로 선언
  • 클래스가 아니므로 생성자를 선언할 수 없다. 그러므로 객체를 생성할 수 없다.
  • 선언 예시
      mixin MyMixin{  
          int mixinData = 10;  
          void mixInFun() {  
          print('MyMixin... myFun()...');  
          }  
      }
    

(2) 믹스인 사용하기

  • 클래스는 다중 상속 불가 → 믹스인 멤버 이용
      class MySuper {
          int superData = 20;
          void superFun() {
              print('MySuper... superFun()');
          }
      }
    	
      class MyClass extends MySuper with MyMixin {
          void sayHello() {
              print('class data : $superData, mixin data : $mixinData');
              mixInFun();
              superFun();
          }
      }
    	
      main() {
          var obj = MyClass();
          obj.sayHello();
      }
    
  • 믹스인 타입 객체 선언(MyMixin)
      main() {  
      var obj = MyClass();  
      // MyMixin obj2 = MyClass();
    
      if (obj is MyMixin) {  
      print('obj is MyMixin');  // obj is MyMixin
          } else {  
      print('obj is not MyMixin');  
          }  
      }
    

(3) 믹스인 사용 제약

  • 믹스인 타입 제한 : on 예약어로 해당 클래스 지정(상속받는 자식 클래스도 가능)
      mixin MyMixin on MySuper {}
      class MySuper {}
      class MyClass extends MySuper with MyMixin {} // 성공
      class MySomeClass with MyMixin {}  // 오류
    

댓글남기기