어제 강의에서 다 못 들었던 부분 정리
final 한 번만 할당 가능 (= const)
import 다른 Dart 파일이나 패키지를 불러올 때 반드시 최상단에 위치
| 자료형 | 설명 | 예제 |
|---|---|---|
| int | 정수 | int age = 20; |
| double | 실수 | double height = 172.5; |
| String | 문자열 | String name = "홍길동"; |
| bool | 참/거짓 | bool isStudent = true; |
| 키워드 | 설명 | 예제 |
|---|---|---|
| var | 한 번만 타입 지정, 이후 재할당 가능 | var score = 100; |
| final | 한 번만 할당 가능, 이후 변경 불가 | final name = "현서"; |
| import | 다른 Dart 파일이나 패키지 불러오기, 최상단 위치 | import 'dart:math'; |
반환타입 함수명(매개변수) { 실행할 코드들 }
// void : 결과값 없이 실행만 한다는 뜻
// sayHello(): 함수 이름 (블록의 이름)
void sayHello() {
print('안녕하세요!');
}
void main() {
printName("홍길동");
}
void printName(String name) {
print("이름은 $name입니다");
}
const printName = (name) => {
console.log(`이름은 ${name}입니다`);
};
printName("홍길동");
props 느낌같음
순서대로 값을 전달해야하는 방식
void greet(String name, int age) {
print('이름: $name, 나이: $age');
}
void main() {
greet('현서', 24); // 순서 중요!
}
required 키워드로 꼭 필요한 값 지정 가능void greet({required String name, int age = 0}) {
print('이름: $name, 나이: $age');
}
void main() {
greet(name: '현서', age: 24);
}
💡 순서 바꿔도 ok, 값을 넣어주면 기본으로 그 값을 넣는다는 뜻
void greet(String name, [int age = 0]) {
print('이름: $name, 나이: $age');
}
void main() {
greet('현서'); // 나이 생략 가능
greet('현서', 24); // 나이도 전달 가능
}
💡 [] 대괄호 안 값은 생략 가능
프로그램이 어떤 조건을 만족할 때만 실행되는 코드 블록 만일 ~~ 라면
| 키워드 | 설명 |
|---|---|
| if | 조건이 true일 때 실행 |
| else if | 다른 조건 추가할 때 |
| else | 위 조건에 모두 해당하지 않을 때 |
void main() {
int age = 20;
if (age >= 18) {
print('성인입니다.');
} else {
print('미성년자입니다.');
}
}
// 성인입니다. 출력
<void main() {
int score = 85;
if (score >= 90) {
print('A');
} else if (score >= 80) {
print('B');
} else {
print('C 이하');
}
} //B
자바 스크립트랑 비슷한듯
const grade = (score) => {
if (score >= 90) {
console.log("A");
} else if (score >= 80) {
console.log("B");
} else {
console.log("C 이하");
}
};
grade(85) //B
똑같은 코드를 여러 번 반복할 때 사용
반복 횟수가 명확할 때 사용
for (변수 선언초기값; 조건; 증감식) { ... }
void main() {
for (int i = 1; i <= 5; i++) {
print('안녕하세요 $i');
}
}
// 출력
안녕하세요 1
안녕하세요 2
안녕하세요 3
안녕하세요 4
안녕하세요 5
지겹게 공부했던 for문 다시 보니 반갑..^^
반복 횟수가 조건에 따라 달라질 때 사용
while (조건) { ... }
void main() {
int i = 1;
// true 일동안 실행
while (i <= 5) {
print('while 반복: $i');
i++; //증감 따로 작성
}
}
void main() {
for (int i = 1; i <= 9; i++) {
print("2 * $i = ${2 * i}");
}
}
i 변수 하나만 쓸 때는 $i 이렇게 써도 가능 수식 쓸 때는 묶기 ${}
Class(키워드) 클래스명 { 속성, 메서드 정의 }
→ 대문자로 시작
<class Person {
// 속성
String name;
int age;
// 생성자 : 클래스를 생성(객체로 만드는 일)할 때 초기화(속성에 값을 할당)하는 특별한 함수
Person(this.name, this.age);
/// 메서드(클래스 안에 있는 함수)
void printIntro() {
print("내이름은 $name! $age살 이지!");
}
}
void main(){
// 객체화
Person joy = Person("조이", 29);
}

마린을 개발한다고 가정
이걸 토대로 만든 설계도
// hp와 attack은 마린이 가지고 있는 속성(변수)
// move()와 shoot()는 마린이 할 수 있는 행동(메서드)
class Marine {
int hp = 40;
int attack = 6;
void move(String direction) {
print("마린이 $direction 방향으로 이동합니다.");
}
void shoot() {
print("탕탕! 마린이 공격합니다. 공격력: $attack");
}
}
게임을 시작하면 마린에 대한 정보(설계도)만 존재
Marine marine1 = Marine();
실제 마린을 만들어 내는 행위 ⇒ 객체화
클래스를 생성(객체로 만드는 일)할 때 초기화(속성에 값을 할당)하는 특별한 함수
함수의 파라미터와 마찬가지로 네임드 파라미터 가능
클래스 이름과 동일한 이름을 가진 가장 기본적인 생성자
class Person {
String name;
int age;
// 기본 생성자
Person(this.name, this.age);
}
class Person2 {
String name;
int age;
bool? isMale;
// 네임드 파라미터를 사용하는 기본생성자
Person2({required this.name, required this.age, this.isMale});
}
void main(){
Person p1 = Person("지드래건", 30);
// Person p2 = Person(30, "지드래건"); // 불가능
Person2 pp = Person2(name: "지드래건", age: 30);
Person2 pp2 = Person2(age: 30, name: "지드래건"); // 가능
// nullable isMale
Person2(name: "지드래건", age: 30); // 가능
Person2(name: "지드래건", age: 30, isMale: true);
// Person2(name: "지드래건", isMale: true); // 불가능
}
class Person은 위치 기반 파라미터
위치가 바뀌면 불가능
class Person2은 네임드 파라미터
위치 바뀌어도 가능
isMale은 nullable(bool?)이라서 생략 가능
이름이 지정되어 있는 생성자
이를 통해 코드의 가독성을 높이고 특정 목적에 맞는 객체 생성을 용이하게 함
기본 생성자 외에 이름을 붙여서 여러 개 만들 수 있는 생성자
class Marine {
int hp = 40;
int attack = 6;
void move(String direction) {
print("마린이 $direction 방향으로 이동합니다.");
}
void shoot() {
print("탕탕! 마린이 공격합니다. 공격력: $attack");
}
Marine();
Marine.hero() : hp = 100, attack = 6;
}
void main(List<String> arguments) {
Marine hero = Marine.hero();
Marine normal = Marine();
}
Marine();
Marine.hero() : hp = 100, attack = 6;
Marine();
기본 생성자
아무 설정 안 함 → 위에서 선언한 기본값(hp = 40, attack = 6) 그대로 사용.
Marine.hero();
네임드 생성자 : 뒤에서 멤버 변수 값을 새로 초기화함.
→ hp = 100, attack = 6으로 설정.
즉, “영웅 마린”을 따로 만드는 특별한 생성자.
void main(List<String> arguments) {
Marine hero = Marine.hero();
Marine normal = Marine();
}
Marine.hero() → hp=100짜리 영웅 마린 생성.
Marine() → hp=40짜리 일반 마린 생성.
부모 클래스의 특징을 물려받는 것 extends
🚗
일반 자동차(Car) 설계도를 기반으로, 전기차(ElectricCar)를 만들고 싶을 때
기존 Car 클래스의 특징을 그대로 물려받고 (바퀴 4개, 운전)
전기차만의 특징을 추가 (배터리 값, 충전)
class Car {
int wheels = 4;
void drive() {
print('브르릉! 자동차가 달립니다.');
}
}
class ElectricCar extends Car {
int batteryLevel = 100;
void charge() {
print('충전 중... 배터리 레벨: $batteryLevel%');
}
}
부모 클래스의 메서드를 재정의 할 수 있음
class ElectricCar extends Car {
int batteryLevel = 100;
// 재정의 했음을 알려주는 Annotation(어노테이션)
// 코드를 위한 메타 데이터
// @override -> 실행에는 영향을 주지 않음, 협업시 헷갈리지 않게
void drive() {
print('위이잉');
}
void charge() {
print('충전 중... 배터리 레벨: $batteryLevel%');
}
}
void main() {
Car c = Car();
c.drive();
Car c2 = ElectricCar();
if(c2 is ElectricCar){
(c2 as ElectricCar).charge();
}
}
Car c2 = ElectricCar();
부모 타입 변수에 자식 객체를 넣은 것(업캐스팅)
if (c2 is ElectricCar)
c2.charge를 사용하면 오류
is 연산자는 타입 확인 연산자
→ c2가 실제로 ElectricCar의 인스턴스인지 확인.
true면 아래 코드 실행
(c2 as ElectricCar).charge();
as는 형변환 연산자
→ c2를 ElectricCar 타입으로 변환.
변환 후 ElectricCar만 가진 메서드(charge())를 호출할 수 있게 됨
클래스 앞에 abstract 키워드를 사용하고 미완성 메소드(추상 메소드)를 포함할수 있으며 직접 객체를 생성할 수 없고 반드시 상속을 통해 완성해야 하는 클래스
abstract class Hero {
// 구현체(중괄호)가 없는 메소드 : 추상 메소드
// 반드시 상속받은 클래스에서 상속받아 구현해주어야함
void skill();
}
구현을 강제하기 위해 사용
abstract class Car {
int wheels = 4;
void drive() {
print('브르릉! 자동차가 달립니다.');
}
// 자동차의 깜빡이
// 반드시 있어야 하지만 자동차 별로 다름
void turnSignal(); // 추상 메서드
}
Car는 “자동차”라는 공통 설계도 역할
drive()는 모든 자동차가 공통으로 가지는 행동이니까 구현되어 있음
turnSignal()은 모든 자동차에 있어야 하지만 브랜드마다 다르게 작동하므로 “구체적 구현 없이” 선언만 되어 있음 → 추상 메서드
즉 Car는 “자동차는 깜빡이가 반드시 있어야 한다”라는 규칙을 강제하는 클래스
class AudiCar extends Car {
void turnSignal() {
print(">>>");
print("<<<");
}
}
class TeslaCar extends Car {
void turnSignal() {
print("<<<");
print("]]]");
}
}
class BusanCar extends Car {
void turnSignal() {
print("");
}
}
실행 예시1
void main() {
AudiCar a = AudiCar();
TeslaCar t = TeslaCar();
BusanCar b = BusanCar();
a.drive(); // 브르릉! 자동차가 달립니다.
a.turnSignal(); // >>> <<<
t.drive(); // 브르릉! 자동차가 달립니다.
t.turnSignal(); // <<< ]]]
b.turnSignal(); // (출력 없음)
}
실행 예시2
void main() {
Car car1 = AudiCar();
Car car2 = TeslaCar();
Car car3 = BusanCar();
car1.turnSignal(); // >>> <<<
car2.turnSignal(); // <<< ]]]
car3.turnSignal(); // (출력 없음)
}
static은 클래스에 속하는 공유변수 또는 공유함수를 정의할 때 사용하는 키워드
class Student {
// 일반(static이 아닌) 멤버 변수: 인스턴스마다 다른 값을 가집니다.
String name;
// static 멤버 변수: 클래스 전체가 공유하는 값입니다.
static String schoolName = "ABC 고등학교";
// static 메서드: 인스턴스 없이 바로 호출할 수 있습니다.
static void displaySchoolName() {
print("우리 학교는 $schoolName 입니다.");
}
Student(this.name);
}
student1.name, student2.name 처럼 접근해야 함static String schoolNameStudent.schoolName으로 접근 (객체 필요 없음)static void displaySchoolName()(Student.displaySchoolName())this 사용 불가능 (객체 정보가 없으니까)💡 실행 과정
void main() {
// static 멤버에 접근하는 방법
// 객체를 만들 필요 없이, 클래스 이름으로 바로 접근합니다.
print("학교 이름: ${Student.schoolName}");
Student.displaySchoolName();
// 일반 멤버에 접근하는 방법 (객체를 생성해야만 접근 가능)
Student student1 = Student("김철수");
Student student2 = Student("박영희");
print("${student1.name} 학생의 학교 이름은 ${Student.schoolName}입니다.");
print("${student2.name} 학생의 학교 이름은 ${Student.schoolName}입니다.");
student1.name → "김철수"
student2.name → "박영희"
하지만 두 객체 모두 Student.schoolName을 공유함
// 만약 학교 이름이 바뀌면?
Student.schoolName = "새로운 학교"; // static 변수는 한 번 바꾸면 모두에게 적용됩니다.
print("${student1.name} 학생의 새로운 학교 이름은 ${Student.schoolName}입니다.");
print("${student2.name} 학생의 새로운 학교 이름은 ${Student.schoolName}입니다.");
}
무엇이든 담을 수 있는 바구니
사과 바구니, 바나나 바구니, 책 바구니 따로 구입 X
다용도 바구니 구입** : 사과, 바나나, 책 모두 보관 가능
class IntBox {
int content;
Box(this.content);
}
class DoubleBox {
double content;
Box(this.content);
}
class StringBox {
String content;
Box(this.content);
}
// T는 타입 자리 표시자
// 사용할 때 어떤 타입을 쓸지 나중에 지정합니다.
class Box<T> {
T content;
Box(this.content);
}
void main(){
var stringBox = Box<String>("Apple"); // content는 String
var intBox = Box<int>(123); // content는 int
}
class Box → T는 타입 변수
Box → T가 String으로 바뀜
Box → T가 int로 바뀜
같은 클래스 로직으로 여러 타입을 처리할 수 있음
T 대신에 어떠한 이름을 써도 무관
관습적으로 많이 써와서 사용
| 이름 | 의미 | 사용 예 |
|---|---|---|
T | Type (가장 일반적인 타입 하나) | Box<T> |
E | Element (List, Set 등에 자주 씀) | List<E> |
K | Key (Map의 key 타입) | Map<K, V> |
V | Value (Map의 value 타입) | Map<K, V> |
S, U, etc. | 기타 타입 여러 개 필요할 때 | Pair<S, T> |
💡 함수에도 사용 가능
T echo<T>(T value) {
return value;
}
void main() {
print(echo<int>(42)); // 42
print(echo<String>("Hi")); // Hi
}
원래 알고 있던 클래스 개념과는 새로운 개념이라 좀 더 이해하고 공부하면 좋을듯
앞으로 flutter에서 배운 부분들이 어떻게 사용될지 감이 안 잡히지만 열심히 해보는 걸로..!