[Dart/Flutter] Dart 언어 배우기 5

ryan·2021년 7월 26일
1

Dart/Flutter

목록 보기
11/21
post-thumbnail

링크


21. Class Static Keyword

void main() {
  // #3
  Employee seulgi = new Employee('슬기'); 
  Employee chorong = new Employee('초롱');

  // #4
  seulgi.printNameAndBuilding(); // 제 이름은 슬기입니다. null 건물에서 근무하고 있습니다.
  chorong.printNameAndBuilding(); // 제 이름은 초롱입니다. null 건물에서 근무하고 있습니다.

  // #5
  // static 키워드를 가진 변수를 바꾸려면 클래스에 직접 접근해야한다
  Employee.building = '여의도 위워크';

  seulgi.printNameAndBuilding(); // 제 이름은 슬기입니다. 여의도 위워크 건물에서 근무하고 있습니다.
  chorong.printNameAndBuilding(); // 제 이름은 초롱입니다. 여의도 위워크 건물에서 근무하고 있습니다.

  // #6
  // 만약에 회사를 이전하게 되었다면?
  Employee.building = '을지로 위워크';

  seulgi.printNameAndBuilding(); // 제 이름은 슬기입니다. 을지로 위워크 건물에서 근무하고 있습니다.
  chorong.printNameAndBuilding(); // 제 이름은 초롱입니다. 을지로 위워크 건물에서 근무하고 있습니다.
}

// Static Keyword: 인스턴스에 귀속이 되지않고, 클래스에 통째로 무언가(함수 또는 변수)가 귀속이 될 때 사용한다.
// 즉, Static 키워드를 사용하면 클래스에 귀속되고, 그렇지 않으면 인스턴스에 귀속된다.
// 따라서, 변수가 인스턴스에 귀속되었을 경우 각각의 인스턴스별로 모든 변수값을 바꿔줘야하지만,
// static 키워드로 선언을 하면 한 번만 바꾸면 된다.

// 아무리 많은 인스턴스를 생성하더라도, static 키워드는 한 번만 변경해주면
// 모든 인스턴스의 building이라는 변수가 다 변경이 된다

// 만약에 static 키워드를 사용하지않았다면 각각 인스턴스 별로 모두 building을 변경해줘야하는 사태가 발생
// seulgi.building = '을지로 위워크';
// chorong.building = '을지로 위워크';

// #0
// 직원
// 근무하고있는 건물 - 모든 직원들이 다 같음, Static Keyword 사용하기에 딱 좋아!
// 직원의 이름 - 사람마다 다 다름
class Employee {
  // #1
  static String? building;
  String name;

  // 생성자
  Employee(String name) : this.name = name;

  void printNameAndBuilding() {
    // #2
    // ${this.building}이라고 작성 시, 에러가 발생한다. 왜냐하면 static 키워드는 인스턴스에 귀속이 되지않기때문이다
    print('제 이름은 ${this.name}입니다. $building 건물에서 근무하고 있습니다.');
  }
}

22. Class super와 this의 차이

void main() {
  Engineer codeFactory = Engineer(
    languages: ['Dart', 'Java', 'JavaScript'],
    name: '코드팩토리',
    building: '여의도 위워크',
  );

  print(codeFactory.name); // 코드팩토리
  print(codeFactory.building); // 여의도 위워크
  print(codeFactory.languages); // [Dart, Java, JavaScript]

  codeFactory.sayInfo(); // 저의 이름은 코드팩토리 입니다. 제가 근무하는 건물은 여의도 위워크 입니다. 제가 사용할 수 있는 언어들은 Dart, Java, JavaScript 입니다.
}

// 직원
class Employee {
  final String building;
  final String name;

  Employee(
    String building,
    String name,
  )   : this.building = building,
        this.name = name;
}

// 엔지니어
// 사용할줄 아는 언어 - 리스트로
class Engineer extends Employee {
  List<String> languages;

  Engineer({
    required List<String> languages,
    required String name,
    required String building,
  })  : this.languages = languages,
        super(
          building,
          name,
        );

  void sayInfo() {
    // #1 super는 부모 클래스의 생성자를 뜻하므로 super.name을 쓰는 게 맞는 거 같은데,
    // #2 부모 클래스에서 만들어진 building은 super가 아닌 this를 사용해도 문제가 없을까?
    // #3 상속(inheritace)에 의해 자식 클래스는 부모 클래스의 모든 것을 상속 받는다. 그래서 모든 것을
    // 다 상속 받은 것처럼 자식 클래스 Engineer 안에서는 name과 building의 값이 실제로
    // Engineer 클래스 안에 존재하는 것처럼 보여진다
    // #4 그래서 super.name이 Employee(부모) 클래스를 가리키는 거지만, this.building이라고 작성을 해도
    // Engineer 클래스 안에 있는 building이라 값이 Employee라는 부모 클래스에서 상속 받은 property를
    // 그대로 가져와서 사용할 수도 있는 것이다. 따라서, 이런 경우에는 super와 this가 같은 값을 갖는다.
    print(
        '저의 이름은 ${super.name} 입니다. 제가 근무하는 건물은 ${this.building} 입니다. 제가 사용할 수 있는 언어들은 ${this.languages.join(', ')} 입니다.');
    // join 메서드는 string으로 바꿔준다.
  }
}

자식 클래스에서 부모 클래스의 변수를 덮어씌울 수도 있다.

void main() {
  Engineer codeFactory = Engineer(
    languages: ['Dart', 'Java', 'JavaScript'],
    name: '코드팩토리',
    building: '여의도 위워크',
  );

  print(codeFactory.name); // null
  print(codeFactory.building); // 여의도 위워크
  print(codeFactory.languages); // [Dart, Java, JavaScript]

  codeFactory.sayInfo(); // 저의 이름은 코드팩토리 입니다. 제가 근무하는 건물은 여의도 위워크 입니다. 제가 사용할 수 있는 언어들은 Dart, Java, JavaScript 입니다.
  codeFactory.sayName(); // this.name은 null, super.name은 코드팩토리
}

// 직원
class Employee {
  final String? building;
  final String? name;

  Employee(
    String? building,
    String? name,
  )   : this.building = building,
        this.name = name;
}

// 엔지니어
// 사용할줄 아는 언어 - 리스트로
class Engineer extends Employee {
  List<String>? languages;
  // 만약에 같은 이름의 변수를 새로 선언하면, super.name과 this.name이 다른 것을 볼 수 있다
  String? name;

  Engineer({
    required List<String>? languages,
    required String? name,
    required String? building,
  })  : this.languages = languages,
        super(
          building,
          name,
        );

  void sayInfo() {
    print(
        '저의 이름은 ${super.name} 입니다. 제가 근무하는 건물은 ${this.building} 입니다. 제가 사용할 수 있는 언어들은 ${this.languages?.join(', ')} 입니다.');
  }

  void sayName() {
    print('this.name은 ${this.name}, super.name은 ${super.name}');
  }
}

23. Class interface

Interface

  • class가 꼭 선언해야하는 메서드와 변수들을 지정하게 해준다.
  • Dart에서는 Interface를 선언하는 경우가 다른 언어들과 비교해서 없는 편이다.

언제 Interface를 사용할까?

  • class를 선언할 때, 비슷한 성격을 가지고 있는 class들은 어떤 특정한 변수와 함수를 가지고 있으면 좋겠다!라는 가정이 있을 때, Interface를 사용한다.
void main() {
  BoyGroup bts = new BoyGroup('BTS');

  bts.sayName();

  GirlGroup redVelvet = new GirlGroup('레드벨벳');

  redVelvet.sayName();
}

// #1 Interface 선언
class IdolInterface {
  // IdolInterface에서 BoyGroup과 GirlGroup에서 필수로 지정해야하는 변수와 함수(메서드)를 넣는다
  String? name;

  void sayName() {}

  // Interface에서는 어떤 함수를 지정해라~ 라는 것만 말해주는 것이기때문에 함수의 body를 작성할 필요가 없다
  // void noBody() {
  //  print('${this.name}');
  //}
}

// #2 Interface 사용 => implements 키워드를 사용 + class(interface가 된다)
class BoyGroup implements IdolInterface {
  String? name;

  BoyGroup(String name) : this.name = name;

  void sayName() {
    print('제 이름은 ${this.name} 입니다.');
  }
}

class GirlGroup implements IdolInterface {
  String? name;

  GirlGroup(String name) : this.name = name;

  void sayName() {
    print('제 이름은 ${this.name} 입니다.');
  }
}

Q. Interface 말고, 상속으로 해결하면 되는 거 아닌가?

A. 맞는 말입니다.

void main() {
  BoyGroup bts = new BoyGroup('BTS');

  bts.sayName();

  GirlGroup redVelvet = new GirlGroup('레드벨벳');

  redVelvet.sayName();
}

class IdolInterface {
  String? name;

  void sayName() {}
}

// Q. Interface 대신, 상속을 활용하면 되는 거 아닌가?
class Idol {
  final String? name;

  Idol(String name) : this.name = name;

  void sayName() {}
}

class BoyGroup extends Idol {
  // 이렇게 하면 똑같은 거 아닌가요?
  // 똑같지만, 뉘앙스가 다르다.
  // #1 상속에서 오는 것은 '부모에 있는 것들을 전부 상속을 받는 것'이고,
  BoyGroup(String name) : super(name);

  @override
  void sayName() {
    print('제 이름은 ${this.name} 입니다.');
  }
}

// #2 Interface 같은 경우에는 '무조건 선언해줘!'라고 강제하는 느낌이다.
// #3 상속을 써도 될 거 같고, Interface를 써도 될 거 같은 상황에서는
// 어떤 것을 써도 상관이 없다. 코딩은 언제나 유연하게, 가장 적합한 것을 사용하는 것이기때문에
// 어떤 것을 사용해도 논리적으로 맞다! 라면 둘 중 어떤 것을 사용해도 문제가 없다.
// #4 강제하고 싶다면 Interface를 사용하는 게, 더 좋은 방향일 수 있다.
class GirlGroup implements IdolInterface {
  String? name;

  GirlGroup(String name) : this.name = name;

  void sayName() {
    print('제 이름은 ${this.name} 입니다.');
  }
}

24. Class Cascade Operator

void main() {
  // #1 기본
  Idol idol = Idol('슬기', '레드벨벳');

  // * '.' 점 하나는 메서드를 실행할 때, 사용할 수 있다.
  // 메서드가 적으면 문제가 없는데, 여러개라면? Cascade Operator을 활용하자
  idol.sayName(); // 제 이름은 슬기 입니다.
  idol.sayGroup(); // 저는 레드벨벳 소속입니다.

  // * '..' 점 두개는 이어서 메서드를 여러개 실행할 때, 사용할 수 있다.
  // #2 Cascade Operator 활용
  idol
    ..sayName() // 제 이름은 슬기 입니다.
    ..sayGroup(); // 저는 레드벨벳 소속입니다.

  // #3
  Idol('슬기', '레드벨벳')
    ..sayName() // 제 이름은 슬기 입니다.
    ..sayGroup(); // 저는 레드벨벳 소속입니다.
}

// Cascade Operator
// => idol..sayName(); '..' 쩜 2개?

class Idol {
  String name;
  String group;

  Idol(String name, String group)
      : this.name = name,
        this.group = group;

  void sayName() {
    print('제 이름은 ${this.name} 입니다.');
  }

  void sayGroup() {
    print('저는 ${this.group} 소속입니다.');
  }
}
profile
👨🏻‍💻☕️ 🎹🎵 🐰🎶 🛫📷

0개의 댓글