1. 클래스 ( Class )
클래스는 객체(Object)를 만들기 위한 설계도다.
객체는 클래스 인스턴스(Instance)로, 실제로 생성된 데이터 덩어리라고 보면 된다.
✔️ 기본 클래스 만들기
Car 클래스를 새성하고, brand, speed 속성(변수)를 추가
accelerate() 메서드로 속도 증가 , 객체 생성 후 메서드 호출
class Car { //클래스 생성
String brand = "BMW";
int speed = 0;
void accelerate() { //메서드 생성
speed += 10;
print("$brand의 속도: $speed km/h");
}
}
void main() {
Car myCar = Car(); // 객체 생성
myCar.accelerate(); // 메서드 호출
myCar.accelerate(); // 속도 증가
}
2. 생성자 ( Constructor )
클래스에 생성자(Constructor)를 추가하면 객체 생성 시 값을 초기화 할 수 있다.
✔️ 생성자 사용하기
생성자로 속성 초기화, 객체 생성시 값을 전달하여 초기 속도 설정 가능
class Car {
String brand;
int speed;
Car(this.brand, this.speed); // 생성자
void accelerate() {
speed += 10;
print("$brand의 속도: $speed km/h");
}
}
void main() {
Car myCar = Car("Tesla", 50); // 객체 생성 + 초기값 설정
myCar.accelerate();
}
3. Getter & Setter
Getter : 객체의 속성을 가져오는 함수
Setter : 객체의 속성을 변경하는 함수
✔️ Getter & Setter 사용
class Car {
String brand;
int _speed = 0; // private 변수, 외부에서 직접 접근 방지 (언더스코어 _ 사용)
Car(this.brand);
int get speed => _speed; // Getter
set speed(int value) {
if (value >= 0) {
_speed = value; // Setter
}
}
void accelerate() {
_speed += 10;
print("$brand의 속도: $_speed km/h");
}
}
void main() {
Car myCar = Car("Hyundai");
myCar.accelerate();
myCar.speed = 100; // Setter 사용
print("현재 속도: ${myCar.speed} km/h"); // Getter 사용
}
4. 상속 ( Inheritance )
상속(Inheritance)을 사용하면 기존 클래스의 기능을 재사용할 수 있다.
✔️ 상속하기
extends 를 사용하고, 부모 클래스의 기능을 자식 클래스에서 그대로 사용 가능하다.
class Vehicle {
String brand;
int speed = 0;
Vehicle(this.brand);
void accelerate() {
speed += 10;
print("$brand의 속도: $speed km/h");
}
}
// Car 클래스가 Vehicle을 상속받음
class Car extends Vehicle {
int fuel = 100;
//자식 클래스 생성자에서 부모 클래스의 생성자를 호출
Car(String brand) : super(brand);
void refuel() {
fuel = 100;
print("$brand의 연료가 가득 찼습니다.");
}
}
void main() {
Car myCar = Car("Toyota");
myCar.accelerate(); // 부모 클래스의 메서드 사용 가능
myCar.refuel(); // 자식 클래스의 메서드 사용 가능
}
Car(String brand) : super(brand);는 부모 클래스의 생성자(Vehicle)을 호출하는 역할은 한다.
즉, 자식 클래스(Car)가 생성될 때, 부모 클래스(Vehicle)의 생성자를 실행해서 초기값을 설정하는 내용이다.
- Car("Toyota")를 호출하면
→ Car 생성자가 실행됨 - Car(String brand) : super(brand); 실행
→ super(brand);를 통해 부모 클래스(Vehicle)의 생성자인 Vehicle(this.brand); 실행 - Vehicle 클래스의 brand가 "Toyota"로 설정됨
- 즉, Car 객체가 생성되면서 brand 값이 부모 클래스에도 전달됨
또한 super(brand); 없이 부모클래스의 속성을 초기화하지 않으면 오류가 발생할 수도 있다.
5. 인터페이스 ( Interface ) & 추상 클래스 ( Abstract Class )
인터페이스(Interface)는 특정 기능을 강제하도록 만들어진 틀이며, 추상 클래스(Abstract Class)는 상속을 위한 기본 구조를 제공하는 클래스다.
✔️ 추상 클래스 사용하기
abstract class 로 추상클래스를 선언하고, 보통 구현 없이 선언만 한다. 그리고 이후 자식 클래스에서 @override를 통해 새로운 기능을 구현한다.
abstract class Animal {
void makeSound(); // 추상 메서드 (구현 없이 선언만 함)
}
class Dog extends Animal {
@override
void makeSound() {
print("멍멍!");
}
}
class Cat extends Animal {
@override
void makeSound() {
print("야옹!");
}
}
void main() {
Dog dog = Dog();
dog.makeSound(); // "멍멍!" 출력
Cat cat = Cat();
cat.makeSound(); // "야옹!" 출력
}
dog와 cat 객체를 생성하고, Animal 을 상속받아, 추상메서드를 override 하여 만든 내용.
✔️ 추상 클래스 (Abstract Class)를 사용하는 이유
추상 클래스는 공통된 기능을 정의하고, 자식 클래스가 반드시 구현해야 할 메서드를 강제하기 위해 사용한다.
즉, 일반 클래스처럼 직접 객체를 생성할 수 없고, 상속받는 클래스에서 구현해야 하는 틀을 제공하는 역할이다.
- 공통 기능 정의 + 특정 기능 강제 (Dog, Cat이 makeSound()를 반드시 구현해야 함)
- 객체 생성 방지 (Shape처럼 직접 인스턴스를 만들 수 없음)
- 유지보수와 확장성을 향상 (Animal을 상속받으면 makeSound()를 구현해야 하므로 일관성 유지)
아래 예제들을 통해서 내용을 이해하고 넘어가면 좋을 것 같다.
abstract class Animal {
void makeSound(); // 추상 메서드 (반드시 구현해야 함)
void eat() {
print("음식을 먹습니다.");
}
}
class Dog extends Animal {
@override
void makeSound() {
print("멍멍!");
}
}
class Cat extends Animal {
@override
void makeSound() {
print("야옹!");
}
}
void main() {
Dog dog = Dog();
dog.makeSound(); // "멍멍!"
dog.eat(); // "음식을 먹습니다."
Cat cat = Cat();
cat.makeSound(); // "야옹!"
cat.eat(); // "음식을 먹습니다."
}
- Animal 추상 클래스에서 makeSound()를 정의하고 구현은 안 함
- 자식 클래스(Dog, Cat)는 makeSound()를 반드시 구현해야 함
- 공통 기능(eat())은 추상 클래스에서 직접 구현 가능
abstract class Shape {
double getArea(); // 추상 메서드 (각 도형마다 면적 계산이 다르므로 구현 없음)
}
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double getArea() {
return 3.14 * radius * radius;
}
}
void main() {
// Shape shape = Shape(); ❌ 오류 발생 (추상 클래스는 직접 객체 생성 불가능)
Circle circle = Circle(5);
print("원의 면적: ${circle.getArea()}");
}
- Shape 클래스는 getArea()라는 공통 메서드를 제공하지만, 직접 객체 생성은 불가능
- 각 도형 클래스(Circle)에서 getArea()를 구현해야 함
✔️ 테스트 예제 코드 1
위에서 진행했던 내용을 토대로 충분한 실습을 진행한 후, 테스트 예제를 진행해보도록 하자.
1. Person 클래스를 만들고, name과 age 속성을 가진 객체를 생성하는 프로그램 만들기
2. Getter & Setter를 활용하여, name과 age를 get으로 가져오고, set으로 age를 수정하게 만들기
3. Setter를 활용할때에 나이 유효성 검사 추가하기 ( 음수 제외 )
4. Person 클래스에 introduce() 메서드를 추가해서 이름과 나이를 출력.
class Person {
String _name;
int _age;
// 생성자
Person(this._name, this._age);
// Getter
String get name => _name;
int get age => _age;
// Setter (유효성 검사 포함)
set age(int value) {
if (value > 0) {
_age = value;
} else {
print("⚠️ 나이는 0보다 커야 합니다.");
}
}
// 소개하는 메서드
void introduce() {
print("안녕하세요! 저는 $_name, $_age살입니다.");
}
}
void main() {
Person p = Person("Kim", 12);
print("${p.name}, ${p.age}"); // Getter 사용
p.age = -5; // 나이 유효성 검사 (잘못된 값)
p.age = 25; // 올바른 값 설정
print("수정된 나이: ${p.age}");
p.introduce(); // 메서드 호출
}
✔️ 테스트 예제 코드 2-1
1. BankAccount 클래스를 만들고, deposit(), withdraw() 메서드를 구현하여 입출금을 처리하는 프로그램 만들기
2. BankAccount 클래스에는 accountNumber(계좌번호)와 balance(잔액)이 있다.
3. deposit(amount) -> 입금 / withdraw(amount) -> 출금 ( 잔액보다 많이 출금할 경우 예외 처리 )
import 'dart:io';
class BankAccount {
String accountNumber;
double _balance;
// 생성자
BankAccount(this.accountNumber, this._balance);
// Getter (잔액 조회)
double get balance => _balance;
// 입금 메서드
void deposit(double amount) {
if (amount > 0) {
_balance += amount;
print("💰 $amount원이 입금되었습니다. 현재 잔액: $_balance원");
} else {
print("⚠️ 입금 금액은 0보다 커야 합니다.");
}
}
// 출금 메서드
void withdraw(double amount) {
if (amount > _balance) {
print("🚨 출금 실패! 잔액이 부족합니다.");
} else if (amount > 0) {
_balance -= amount;
print("💸 $amount원이 출금되었습니다. 현재 잔액: $_balance원");
} else {
print("⚠️ 출금 금액은 0보다 커야 합니다.");
}
}
}
void main() {
// 계좌 생성
BankAccount myAccount = BankAccount("123-456-789", 10000);
print("계좌번호: ${myAccount.accountNumber}");
print("현재 잔액: ${myAccount.balance}원");
myAccount.deposit(5000); // 정상 입금
myAccount.withdraw(3000); // 정상 출금
myAccount.withdraw(20000); // 잔액 부족 예외 처리
myAccount.withdraw(-1000); // 잘못된 금액 예외 처리
}
✔️ 테스트 예제 코드 2-2
1. 2-1의 작성한 코드를 토대로 여 계좌를 생성할 수 있도록 List<BankAccount>를 활용해보자( 3일차 내용 )
2. 사용자로부터 계좌 정보를 입력받아 저장 ( stdin 사용 )
3. 계좌 검색 기능 추가 ( 계좌번호로 특정 계좌 찾기 / 3일차 내용 )
4. 입출금 기능은 그대로 유지
import 'dart:io';
class BankAccount {
String accountNumber;
double _balance;
// 생성자
BankAccount(this.accountNumber, this._balance);
// Getter (잔액 조회)
double get balance => _balance;
// 입금 메서드
void deposit(double amount) {
if (amount > 0) {
_balance += amount;
print("💰 $amount원이 입금되었습니다. 현재 잔액: $_balance원");
} else {
print("⚠️ 입금 금액은 0보다 커야 합니다.");
}
}
// 출금 메서드
void withdraw(double amount) {
if (amount > _balance) {
print("🚨 출금 실패! 잔액이 부족합니다.");
} else if (amount > 0) {
_balance -= amount;
print("💸 $amount원이 출금되었습니다. 현재 잔액: $_balance원");
} else {
print("⚠️ 출금 금액은 0보다 커야 합니다.");
}
}
}
// 계좌 목록 저장
List<BankAccount> accounts = [];
// 사용자 입력 함수 (공백 방지)
String getUserInput(String prompt) {
while (true) {
stdout.write(prompt);
String? input = stdin.readLineSync()?.trim();
if (input != null && input.isNotEmpty) {
return input;
}
print("⚠️ 올바른 값을 입력하세요.");
}
}
// 숫자 입력 함수 (예외 처리)
double getAmountInput(String prompt) {
while (true) {
stdout.write(prompt);
String? input = stdin.readLineSync();
try {
double amount = double.parse(input!);
if (amount >= 0) {
return amount;
}
print("⚠️ 금액은 0 이상이어야 합니다.");
} catch (e) {
print("⚠️ 올바른 숫자를 입력하세요.");
}
}
}
// 계좌 검색 함수
BankAccount? findAccount(String accountNumber) {
for (var account in accounts) {
if (account.accountNumber == accountNumber) {
return account;
}
}
return null;
}
void main() {
print("📝 은행 계좌 관리 시스템");
// 계좌 등록
while (true) {
String accountNumber = getUserInput("📌 새 계좌번호를 입력하세요 (종료: q): ");
if (accountNumber == "q") break;
double initialBalance = getAmountInput("💰 초기 잔액을 입력하세요: ");
accounts.add(BankAccount(accountNumber, initialBalance));
print("✅ 계좌가 등록되었습니다! (계좌번호: $accountNumber, 잔액: ${initialBalance}원)");
}
// 계좌 검색 및 입출금 기능
while (true) {
String searchAccount = getUserInput("🔍 계좌번호를 입력하세요 (종료: q): ");
if (searchAccount == "q") break;
BankAccount? account = findAccount(searchAccount);
if (account == null) {
print("🚨 해당 계좌를 찾을 수 없습니다.");
continue;
}
while (true) {
print("\n💳 [계좌: ${account.accountNumber}] 현재 잔액: ${account.balance}원");
print("1. 입금 2. 출금 3. 종료");
String choice = getUserInput("👉 원하는 작업을 선택하세요: ");
if (choice == "1") {
double depositAmount = getAmountInput("💰 입금할 금액을 입력하세요: ");
account.deposit(depositAmount);
} else if (choice == "2") {
double withdrawAmount = getAmountInput("💸 출금할 금액을 입력하세요: ");
account.withdraw(withdrawAmount);
} else if (choice == "3") {
break;
} else {
print("⚠️ 올바른 선택을 해주세요.");
}
}
}
print("📌 프로그램을 종료합니다.");
}
✔️ 테스트 예제 코드 3
1. Shape 추상 클래스를 만들고, Circle과 Rectangle, Triangle 클래스를 추가하여 면적을 계산할 수 있도록 하는 프로그램
2. 1을 입력받을 경우 원 면적 계산, 2를 입력받을 경우 사각형 면적 계산, 3을 입력받을 경우 삼각형 면적 계산
3. Shape 추상 클래스 생성. 해당 추상 클래스는 getArea() 메서드를 가지고 있다.
4. Circle 클래스 : radius(반지름) , getArea() / d원 면적 계산 -> 3.14 * radius * radius
5. Rectangle 클래스 : width(가로) , height(세로), getArea()
6. Triangle 클래스 : base(밑변) , height(높이), getArea() / 삼각형 면적 계산 -> 0.5 * base * height
import 'dart:io';
// 추상 클래스
abstract class Shape {
double getArea(); // 면적 계산 메서드 (구현 없음)
}
// 원 클래스
class Circle extends Shape {
double radius;
Circle(this.radius);
@override
double getArea() {
return 3.14 * radius * radius;
}
}
// 사각형 클래스
class Rectangle extends Shape {
double width, height;
Rectangle(this.width, this.height);
@override
double getArea() {
return width * height;
}
}
// 삼각형 클래스
class Triangle extends Shape {
double base, height;
Triangle(this.base, this.height);
@override
double getArea() {
return 0.5 * base * height;
}
}
// 숫자 입력 함수 (공백 및 예외 처리)
double getNumberInput(String prompt) {
while (true) {
stdout.write(prompt);
String? input = stdin.readLineSync();
try {
double number = double.parse(input!);
if (number > 0) {
return number;
}
print("⚠️ 숫자는 0보다 커야 합니다.");
} catch (e) {
print("⚠️ 올바른 숫자를 입력하세요.");
}
}
}
void main() {
print("📝 도형 면적 계산기");
while (true) {
print("\n1. 원 2. 사각형 3. 삼각형 4. 종료");
stdout.write("👉 원하는 도형을 선택하세요: ");
String? choice = stdin.readLineSync();
if (choice == "1") {
double radius = getNumberInput("🟢 원의 반지름을 입력하세요: ");
Circle circle = Circle(radius);
print("✅ 원의 면적: ${circle.getArea()}");
} else if (choice == "2") {
double width = getNumberInput("🟦 사각형의 가로 길이를 입력하세요: ");
double height = getNumberInput("🟦 사각형의 세로 길이를 입력하세요: ");
Rectangle rectangle = Rectangle(width, height);
print("✅ 사각형의 면적: ${rectangle.getArea()}");
} else if (choice == "3") {
double base = getNumberInput("🔺 삼각형의 밑변을 입력하세요: ");
double height = getNumberInput("🔺 삼각형의 높이를 입력하세요: ");
Triangle triangle = Triangle(base, height);
print("✅ 삼각형의 면적: ${triangle.getArea()}");
} else if (choice == "4") {
print("📌 프로그램을 종료합니다.");
break;
} else {
print("⚠️ 올바른 선택을 해주세요.");
}
}
}
이렇게 클래스와 객체 기초 부분에 대해서 알아보았다. 추가 적인 내용이 필요한 경우에는 댓글을 요청드리고, 틀린 부분이 있다면 이것 또한 댓글로 알려주시면 수정하도록 하겠습니다!
'IT > Dart' 카테고리의 다른 글
Dart_6일차 : 고급 Stream 활용 (listen(), StreamController, Broadcast Stream) (2) | 2025.03.04 |
---|---|
Dart_5일차 : 비동기 프로그래밍 (Future, async/await, Stream) (0) | 2025.02.21 |
Dart_3일차 : 컬렉션(List, Set, Map), 예외처리 (0) | 2025.02.19 |
Dart_2일차 : 제어문과 함수 심화 학습 (1) | 2025.02.17 |
Dart_1일차 : 기본 문법 (2) | 2025.02.16 |