Dart 기본 문법 07

GreenBean·2023년 4월 4일
0
post-thumbnail

Dart

Dart 공식문서
다트 ( Dart ) Getter 와 Setter
다트 ( Dart ) 추상 클래스

Dart 의 Getter 와 Setter

  • 클래스의 멤버 변수를 private 이 아닌 public 으로 선언하면 아무 곳에서나 접근이 가능
    • 의도하지 않은 곳에서 접근하여 값을 변경하는 일이 발생할 수 있음
    • 또한 그런 일이 발생하면 수정하는게 쉽지 않음
      • 여기저기서 참조하고 있으면 어디서 잘못된 값으로 변경했는지 모두 확인해야 하기 때문
    • 멤버 변수 뿐만 아니라 메서드도 마찬가지
  • 이런 일을 방지하고자 클래스의 내부 정보를 공개하지 않도록 하는 것정보 은닉 방법 중 한 가지
    • 이러한 정보 은닉은 바로 캡슐화를 통해서 이뤄짐
    • 특정 멤버 변수에 접근할 수 있는 특별한 기능을 하는 메서드를 만들면 됨
  • 멤버 변수를 private 로 선언하고 해당 변수에 접근할 수 있는 메서드는 public 으로 선언하면 멤버 변수에 직접적으로 접근하는 것을 막을 수 있음
    • 그런 특별한 메서드가 바로 GetterSetter
  • Getter 는 멤버 변수의 값을 가져오는 역할을 하고 Setter 는 값을 쓰는 역할을 함
    • 다트에서는 getter 와 setter 메서드를 각각 get 과 set 이라는 키워드로 사용
# Getter 과 Setter

class person {
  String _name;
  String get name => _name;
  set name(String name) => _name = name;
}
  • private 멤버 변수인 _name 에는 직접 접근이 불가능하지만 public 인 getter 를 통해서 접근 가능
    • 이때 get name 에서 name 이 접근할 때 사용할 이름
    • set 도 get 과 마찬가지로 public 이라 _name 에 값을 할당하기 위해 접근 가능하며 set name 에서 name 은 접근할 때 사용되는 이름
# 예시

class Person {
  late String _name;
  
  String get name => _name;
  set name(String name) => _name = name;
}

main() {
  Person person = Person();
  person.name = "Kim";
  print(person.name);
}

------------------------------------------------------------------------------------------

# 결과

Kim
  • getter 와 setter 를 추가하여 name 이란 이름으로 자연스럽게 _name 에 접근하고 있음
    • name 은 _name 에서 _ 를 빼고 써야 되는 규칙 같은 것이 아님
      • 단순히 임의로 지정해준 이름
      • 아래와 같은 코드도 가능
# 예시

class Person {
  late String _name;
  
  String get hello => _name;
  set setName(String name) => _name = name;
}

main() {
  Person person = Person();
  person.setName = "Kim";
  print(person.hello);
}

------------------------------------------------------------------------------------------

# 결과

Kim
  • getter 를 hello 로 하고 setter 를 setName 이라고 하면 각각 해당 이름을 통해서 _name 에 접근하는 것
    • 다만 name 으로 쓰면 의미 파악이 쉽게 되니 가독성 측면에서 좋을 수 있음
  • 그런데 _name 을 name 으로 wrapping 한 것일 뿐 어디서든 name 으로 참조하여 값을 변경할 수 있지 않을까?
    • getter 와 setter 를 사용하면 어느 정도 제어 가능
    • 예를 들어 아래의 코드처럼 _name 이 절대 null 이 되지 않아야 하는 상황이라면 다음과 같이 null 인 경우에 대한 제어가 가능
# 예시

class Person {
  String _name;
  
  String get name => (_name == null) ? "Lee" : _name;
  
  set name(String name) => (name == null) ? _name = "Park" : _name = name;
}

main() {
  Person person = Person();
  print(person.name);
  person.name = null;
  print(person.name);
}
  • get 을 살펴보면 _name 이 null 일 때 값에 접근하면 Lee 라는 값을 가져옴
    • set 을 보면 _name 에 null 을 할당하려고 하면 Park 라는 값을 할당하여 _name 이 null 이 되는 경우가 없도록 함

Dart 의 추상 클래스

# 추상 클래스

abstract class Person {
  eat();
}
  • 추상 클래스추상 메서드를 가질 수 있는 클래스
    • 필수적으로 포함해야 하는 것은 아님
    • 일반 클래스에는 추상 메서드를 가지고 싶어도 선언할 수 없음
    • 추상 메서드는 미완성된 메서드라고 생각하면 되는데 다시 말해 메서드 선언은 되어 있지만 바디가 정의되지 않으 형태
  • 추상 클래스는 기존 클래스 앞에 추상이란 뜻을 가진 abstract 키워드를 붙여서 표현
  • 추상 클래스는 미완성 클래스이기 때문에 객체를 생성할 수 없음
    • 하지만 참조형 변수의 타입으로는 사용이 가능
    • 추상 클래스를 사용하기 위해서는 일반 클래스에서 implements 키워드로 임플리먼트 한 후에 반드시 추상 메서드를 오버라이딩 해야 함
# 예시

abstract class Person {
  eat();
}

class Developer implements Person {
  
  eat() {
    print("Developer eat a meal");
  }
}

main() {
  Person person = Developer();
  person.eat();
}

------------------------------------------------------------------------------------------

# 결과

Developer eat a meal
  • Developer 클래스는 추상 클래스인 Person 을 임플리먼트 했음
    • 이때 반드시 추상 클래스 Person 의 추상 메서드인 eat() 을 오버라이딩 해야 함
    • 꼭 다른 기능으로 사용하기 위한 재정의가 아니더라도 반드시 Developer 클래스 내에 선언되어 있어야 함
  • main() 함수를 보면 person 객체의 타입으로 추상 클래스인 Person 을 사용
    • 다만 실제 생성되는 객체는 Devloper
    • 이것이 참조형 변수 person 의 타입으로 추상 클래스가 사용 가능한 예
      • 다만 추상 클래스로 객체를 생성하려고 하면 에러 발생
  • 다트에서는 추상 클래스에 반드시 추상 메서드만 존재해야 하는 것은 아님
    • 앞서 말했듯이 추상 메서드가 존재할 수도 있을 뿐
      • 따라서 일반 메서드를 정의할 수도 있고 일반 메서드만 존재할 수도 있음
    • 일반 메서드도 반드시 임플리먼트 하는 클래스에서 재정의 되어야 함
    • 그리고 추상 메서드든 일반 메서드든 임플리먼트 하는 클래스에서 @override 어노테이션 생략이 가능
# 예시

abstract class Person {
  eat();
  
  sleep() {
    print("Person must sleep");
  }
}

class Developer implements Person {
  
  eat() {
    print("Developer eat a meal");
  }
  
  sleep() {
    print("Developer must sleep");
  }
}

main() {
  Person person = Developer();
  person.eat();
  person.sleep();
}

------------------------------------------------------------------------------------------

# 결과

Developer eat a meal
Developer must sleep
  • 위 코드에서 추상 클래스 Person 에 일반 메서드인 sleep() 이 추가되어 있음
    • 또한 Developer 클래스에서 재정의할 때 @override 어노테이션을 생략한 것을 볼 수 있음
    • 이때 예제처럼 일반 메서드인 sleep() 도 반드시 Devloper 클래스에서 재정의 해야 한다는 점은 다시 한번 강조
  • extends 키워드를 사용해서 상속받는 일반 클래스의 경우에는 단 하나의 클래스만 상속 가능
    • 하지만 추상 클래스는 일반 클래스와 다르게 여러 개를 임플리먼트 할 수 있음
# 예시

main() {
  Developer person = Developer();
  person.eat();
  person.sleep();
  person.work();
}

abstract class Person {
  eat();
  
  sleep() {
    print("Person must sleep");
  }
}

abstract class Junior {
  work() {
    print("work hard");
  }
}

class Developer implements Person, Junior {
  
  eat() {
    print("Developer eat a meal");
  }
  
  sleep() {
    print("Developer must sleep");
  }
  
  work() {
    print("Junior developer works hard");
  }
}

------------------------------------------------------------------------------------------

# 결과

Developer eat a meal
Developer must sleep
Junior developer works hard
  • 추상 클래스인 Junior 을 추가하고 Devloper 클래스에서 Person 과 함께 임플리먼트 함
    • 이때도 반드시 Junior 의 메서드를 오버라이딩 해야 함
  • 위 코드에서는 이전에 사용하던 Person 을 참조형 변수의 타입으로 계속 사용할 수 없음
    • 왜냐하면 Junior 의 메서드인 work() 를 참조하려면 Person 타입으로는 불가능하기 때문
    • 만약 person.work() 를 주석 처리하면 Person 타입으로 사용할 수 있음
      • 그러나 추상 클래스를 타입으로 지정하는 것은 일반적이지 않음
  • 따라서 Developer 객체를 생성할 때는 타입도 Developer 로 지정하도록 함

Tip! 요약

  • 추상 클래스와 추상 메서드는 abstract 키워드를 사용
  • 추상 클래스는 참조형 변수의 타입으로 사용할 수 있음
  • 추상 클래스를 임플리먼트 할 때 반드시 메서드를 오버라이딩 해야 함
  • 추상 클래스에 추상 메서드만 존재하는 것은 아님
  • 메서드 오버라이딩 시 @override 어노테이션은 생략 가능
profile
🌱 Backend-Dev | hwaya2828@gmail.com

0개의 댓글