🎯 오늘은 심화 문법 함수와 제네릭, 함수형 프로그래밍, 객체 지향형 프로그래밍에 대해 강의를 들었다.
함수 종류도 많고, 클래스에도 많은 속성이 있어서 학습량이 많았다.
✅ 입력을 받아서 특정 작업을 수행하고, 그 작업에 대한 결과를 반환하는 코드 블록
📁 반환타입
✔ 함수가 값을 함수가 반환하는 값의 데이터 타입
✔ 함수가 값을 반환하지 않는 경우void로 선언한다.
📁 함수 이름
✔ 함수를 호출할 때 사용한다.
📁 매개변수
✔ 함수 내부로 전달되어 코드 블록에서 사용하는 값
📁 실행할 코드
✔ 함수가 호출되면 실행되는 코드 블록
📁 반환값
✔ 함수의 코드 블록이 실행된 후 반환되는 값, 즉 결과값
📋
[반환 타입] [함수 이름]([매개변수 타입] [매개변수 이름]) { return [반환값]; }int add(int a, int b) { return a + b; }반환 타입 :
int
함수 이름 :add
매개변수 타입 :int,int
매개변수 이름 :a,b
반환값 :a + b
📋
[함수 이름]([매개변수 이름]) { return [반환값]; }add(a, b) { return a + b; }❌ 타입 추론이 가능하기 때문에 반환 타입과 매개변수 타입을 생략해도 되지만, 사용하지 않는 것이 좋다.
📋
[반환 타입] [함수 이름]([매개변수 타입] [매개변수 이름]) ⇒ 반환값;int multiply(int a, int b) => a * b;⭕ 반환값이 하나의 표현식으로 나타낼 수 있는 경우만 사용 가능
📁
main()
✔ 최상위 함수로,main()함수 코드 블록에 코드를 넣어야 실행된다.
📁
voidvoid main() { print('Hello'); // Hello }✔ 반환값이 없으면 반환 타입을
void로 설정한다.
📁
List<String>void main(List<String> arguments) { print(arguments); }✔ Flutter 애플리케이션을 개발할 때는 사용하지 않고, Command Line (터미널, 명령 프롬프트) 을 실행할 때 주로 사용한다.
✅ 사용 예제
void main() { int one = 1; int two = 2; print(one + two); // 3 // int three = 3; int four = 4; print(three + four); // 7 // int five = 5; int six = 6; print(five + six); // 11 // int seven = 7; int eight = 8; print(seven + eight); // 15 }void printSum(int a, int b) { print(a + b); } // void main() { printSum(1, 2); // 3 printSum(3, 4); // 7 printSum(5, 6); // 11 }
✅ 클래스나 함수에서 데이터 타입을 일반화하여 다양한 타입을 지원할 수 있게 하는 기능
📁 제네릭 클래스
📁 제네릭 함수
📋[타입 파라미터] [함수 이름]<타입 파라미터>([매개변수]) { … }T getFirstElement<T>(List<T> list) { return list[0]; }✔ 실제
List,Set,Map은 각각
List<E>,Set<E>,Map<E, E>형태로 정의되어 있고, 타입 파라미터인E에 여러 타입이 올 수 있다.
int getFirstNumber(List<int> numbers) { return numbers[0]; } // String getFirstWord(List<String> words) { return words[0]; } // void main() { var numbers = [0, 1, 2, 3]; print(getFirstNumber(numbers)); // 0 // var words = ['a', 'b', 'c']; print(getFirstWord(words)); // a }T getFirstElement<T>(List<T> list) { return list[0]; } // void main() { var numbers = [0, 1, 2, 3]; print(getFirstElement(numbers)); // 0 // var words = ['a', 'b', 'c']; print(getFirstElement(words)); // a }💡 특정 타입만이 아닌, 여러 타입에 대해 동일한 코드를 적용할 수 있어서 재사용성 높은 코드를 짤 수 있다.
✅ 함수의 연속으로 프로그램을 구성하는 방식이다.
. 을 사용해서 여러개의 함수를 하나로 연결하는 방식이다.
int number = -12345 var result = number.abs().toString().contains('3'); print(result); // true✔
abs()는 절댓값을 반환하는 함수, `toString() 은 문자형으로 바꿔주는 함수이다.
String word = 'abcd'; var index = word.toUpperCase().indexOf('B'); print(index); // 1✔
toUpperCase()는 문자열의 모든 문자를 대문자로 바꾼다.
✅ 가변적인 데이터의 사용을 최소화하여 프로그램을 구성하는 방식으로, 출력값이 항상 그 함수의 매개변수(입력값)에만 의존하게 한다.
int add(int a, int b) { return a + b; } // void main() { int result = add(3, 4); print(result); // 7 }✔
add()의 코드 블록을 보면 함수의 결과값이 함수의 매개변수에만 의존하고 있다.
int getTotal(List<int> numbers) { int result = 0; for (var number in numbers) { result += number; } return result; }✅ 같은 입력값에 대해서 항상 같은 출력값이 나오는 함수이다.
💡 같은 값을 입력하면 항상 같은 값이 나오기 때문에 부작용(Side Effect)이 발생할 일이 적다.
🔥 명령형 프로그래밍 방식
int number = 0; // void increaseNumber() { number += 1; } // void main() { print(number); // 0 increaseNumber(); print(number); // 1 }🚨
increaseNumber()안에서 함수 외부의 값을 변경하고 있는 예상하지 못 했던 상황이 발생할 수 있어서 위험하다.
순수 함수가 아닌 명령형 프로그래밍 방식으로 그냥 넘어가도 된다.
✅ 특정 타입의 데이터를 다른 타입의 데이터로 변환하는 함수
📁
toString()int number = 42; var result = number.toString(); print(result); // 42 print(result.runtimeType); // Stringdouble number = 1.5; var result = number.toString(); print(result); // 1.5 print(result.runtimeType); // String✔ 값을
String타입으로 변환한 값을 반환한다.
✔runtimeType은 어떤 타입인지 반환해준다.
📁
int.parse('')String number = '123'; var result = int.parse(number); print(result); // 123 print(result.runtimeType); // intString invalidNumber = 'abcd'; var result = int.parse(invalidNumber); print(result);✔ String 타입의 값을
int타입으로 변환하여 값을 반환한다.
✔ 변환이 어려운 경우에는 오류가 발생한다.
⚡
int.tryParse(’’)String number = '123'; var result = int.tryParse(number); print(result); // 123 print(result.runtimeType); // intString invalidNumber = 'abcd'; var result = int.tryParse(invalidNumber); print(result); // null✔ 형변환에 성공하면
int.parse()와 같이 동작하며, 변환에 실패하면 오류 대신null을 반환한다.
📁
double.parse('')String number = '123.4'; var result = double.parse(number); print(result); // 123.4 print(result.runtimeType); // doubleString invalidNumber = 'abcd'; var result = double.parse(invalidNumber); print(result);✔
int.parse()와 동일하게 String 타입의 값을int타입으로 변환하여 값을 반환한다.
⚡
double.tryParse(’’)String invalidNumber = 'abcd'; var result = double.tryParse(invalidNumber); print(result); // null✔
int.parse()와 동일하게 성공하면double.parse('')같이 작동하고, 실패하면null을 반환한다.
📁
toList()Set<String> fruitSet = {'사과', '오렌지', '수박'}; var fruitList = fruitSet.toList(); print(fruitList); // [사과, 오렌지, 수박] print(fruitList.runtimeType); // List<String>Map<String, String> people = {'Alice': 'Student', 'Bob': 'Teacher'}; var peopleList = people.toList(); print(peopleList); // Error: The method 'toList' isn't defined for the class 'Map<String, String>'.✔ 특정
Collection타입의 값을List로 변환한 값을 반환한다.
❌ Map 의 경우 키와 값 두가지가 존재하기 때문에 변환이 불가능하다.
📁
toSet()List<String> fruitList = ['사과', '오렌지', '수박']; var fruitSet = fruitList.toSet(); print(fruitSet); // {사과, 오렌지, 수박} print(fruitSet.runtimeType); // Set<String>Map<String, String> people = {'Alice': 'Student', 'Bob': 'Teacher'}; var peopleSet = people.toSet(); print(peopleSet); // Error: The method 'toSet' isn't defined for the class 'Map<String, String>'.List<String> fruitList = ['사과', '오렌지', '수박', '사과']; var fruitSet = fruitList.toSet(); print(fruitSet); // {사과, 오렌지, 수박}✔ 특정
Collection타입의 값을Set으로 변환한 값을 반환하며, Set은 중복값을 허용하지 않기 때문에 중복값을 제외하고Set을 반환한다.
❌ 동일하게 Map 변환은 불가능하다.
📁
asMap()List<String> fruitList = ['사과', '오렌지', '수박']; var fruitMap = fruitList.asMap(); print(fruitMap); // {0: 사과, 1: 오렌지, 2: 수박}List<String> fruitList = ['사과', '오렌지', '수박', '사과']; var fruitMap = fruitList.asMap(); print(fruitMap); // {0: 사과, 1: 오렌지, 2: 수박, 3: 사과}Set<String> fruitSet = {'사과', '오렌지', '수박'}; var fruitMap = fruitSet.asMap(); print(fruitMap); // Error: The method 'asMap' isn't defined for the class 'Set<String>'.✔ 특정
Collection타입의 값을Map타입으로 변환한 값을 반환한다.
❌ Set 은 Index 가 없기 때문에 Set 에는 적용하지 못 한다.
Set<String> fruitSet = {'사과', '오렌지', '수박'}; var fruitList = fruitSet.toList(); var fruitMap = fruitList.asMap(); print(fruitMap); // {0: 사과, 1: 오렌지, 2: 수박}💡 단, Set을 List로 변환한 뒤 asMap()으로 변환하면 가능하다.
✅ 함수를 다루는 함수로, Collection 타입의 데이터에 있는 요소를 처리하거나 변환할 때 사용한다.
📁
map()
✔Collection타입인 데이터의 각 요소에 특정 함수를 적용한 새로운Collection타입의 데이터를 반환한다.
📋
map(([매개변수]) { return [매개변수에 적용할 동작] });List<String> fruitList = ['사과', '오렌지', '수박']; var delicious = fruitList.map((fruit) { return '맛있는 $fruit'; }); print(delicious); // (맛있는 사과, 맛있는 오렌지, 맛있는 수박)Set<String> carSet = {'BMW', '현대', '기아'}; var goodCar = carSet.map((car) { return '짱 멋진 $car'; }); print(goodCar); // (짱 멋진 BMW, 짱 멋진 현대, 짱 멋진 기아)Map<String, int> fruits = {'사과': 5, '포도': 2, '귤': 3}; // var delicious = fruits.keys.map((fruit) { return '맛있는 $fruit'; }); print(delicious); // (맛있는 사과, 맛있는 포도, 맛있는 귤) // var numbers = fruits.values.map((number) { return '$number개'; }); print(numbers); // (5개, 2개, 3개)✔
List,Set,Map모두 적용 가능하다.
📋
map(([매개변수]) => [매개변수에 적용할 동작] );List<int> numbers = [1, 2, 3, 4, 5]; var doubledNumbers = numbers.map((n) => n * 2); print(doubledNumbers); // (2, 4, 6, 8, 10)List<String> fruits = ['사과', '오렌지', '수박']; var delicious = fruits.map((fruit) => '맛있는 $fruit'); print(delicious); // (맛있는 사과, 맛있는 오렌지, 맛있는 수박)✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
💡 원본 데이터를 가공하지 않고 특정 함수를 적용한 새로운 데이터를 원본 데이터와 같은 크기로 반환한다.
📁
where()
✔Collection타입의 데이터에 있는 각 요소들을 특정 조건에 넣었을 때, 참인 요소들만 필터링한 새로운Collection타입의 데이터를 반환한다.
📋
where(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.where((number) { return number > 5; }); print(result); // (6, 7, 8, 9, 10)List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.where((fruit) { return fruit.length == 2; }); print(result); // (사과, 수박)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.where((number) { return number.isEven }); print(result); // (2, 4, 6)✔
isEven은 짝수면true를, 홀수면false를 반환하며,number.isEven은number % 2 == 0와 같다고 보면 된다.
📋
where(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.where((n) => n > 5); print(result); // (6, 7, 8, 9, 10)List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.where((f) => f.length == 2); print(result); // (사과, 수박)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.where((n) => n.isEven); print(result); // (2, 4, 6)✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.where((number) { return number > 10; }); print(result); // ()💡 원본 데이터를 가공하지 않고 특정 함수를 적용한 새로운 데이터를 반환하며, 조건식이 참인 요소가 없는 경우에는 빈 값을 반환한다.
📁
firstWhere()
✔Collection타입의 데이터에 있는 각 요소들을 특정 조건에 넣었을 때, 참인 요소들 중 첫번째 요소를 반환한다.
📋
firstWhere(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.firstWhere((number) { return number > 5; }); print(result); // 6List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.firstWhere((fruit) { return fruit.length == 2; }); print(result); // 사과
📋
firstWhere(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.firstWhere((n) => n > 5); print(result); // 6List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.firstWhere((f) => f.length == 2); print(result); // 사과✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
💡 원본 데이터를 가공하지 않고 특정 함수를 적용한 새로운 데이터를 반환한다.
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.firstWhere((number) { return number > 10; }); print(result); // StateError🚨조건식이 참인 요소가 없는 경우 오류가 발생한다.
📁
lastWhere()
✔Collection타입의 데이터에 있는 각 요소들을 특정 조건에 넣었을 때, 참인 요소들 중 마지막 요소를 반환한다.
📋
lastWhere(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.lastWhere((number) { return number > 5; }); print(result); // 10List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.lastWhere((fruit) { return fruit.length == 2; }); print(result); // 수박
📋
lastWhere(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers.firstWhere((n) => n > 5); print(result); // 6List<String> fruits = ['사과', '오렌지', '수박']; var result = fruits.firstWhere((f) => f.length == 2); print(result); // 사과✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
💡 원본 데이터를 가공하지 않고 특정 함수를 적용한 새로운 데이터를 반환한다.
🚨조건식이 참인 요소가 없는 경우 오류가 발생한다.
📁
reduce()
✔Collection타입의 데이터에 있는 요소들을 하나의 값으로 결합한다.
📋
reduce(([매개변수1], [매개변수2]) { return [적용할 동작] });List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.reduce((a, b) { return a + b; });✅ 실행
List<int> numbers = [1, 2, 3, 4, 5]; int result = numbers.reduce((a, b) { print('a : $a'); print('b : $b'); print('a + b : ${a + b}'); print('----------------------'); return a + b; }); print('result : $result'); /* a : 1 b : 2 a + b : 3 ---------------------- a : 3 b : 3 a + b : 6 ---------------------- a : 6 b : 4 a + b : 10 ---------------------- a : 10 b : 5 a + b : 15 ---------------------- result : 15 */
List<String> words = ['다트는 ', '참 ', '재미있군 ?']; var result = words.reduce((a, b) { return a + b; });✅ 실행
List<String> words = ['다트는 ', '참 ', '재미있군 ?']; int result = words.reduce((a, b) { print('a : $a'); print('b : $b'); print('a + b : ${a + b}'); print('----------------------'); return a + b; }); print('result : $result'); /* a : 다트는 b : 참 a + b : 다트는 참 ---------------------- a : 다트는 참 b : 재미있군 ? a + b : 다트는 참 재미있군 ? ---------------------- result : 다트는 참 재미있군 ? */
📋
reduce(([매개변수1], [매개변수2]) => [적용할 동작] );List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.reduce((a, b) => a + b);List<String> words = ['다트는 ', '참 ', '재미있군 ?']; var result = words.reduce((a, b) => a + b);✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.reduce((a, b) => '$a' + '$b'); // Error: A value of type 'String' can't be returned from a function with return type 'int'.❌ 처음 실행될 때 초기값이 정수형인
1이기 때문에 정수형과 문자형을 더할 수 없다.
💡 Collection 타입의 데이터와 같은 타입으로만 반환할 수 있다.
List<int> list = []; var result = list.reduce((a, b) => a + b); // StateError🚨 Collection 타입의 데이터에 요소가 없는 경우에는 오류가 발생한다.
📁
folde()
✔Collection타입의 데이터에 있는 요소들을 하나의 값으로 결합한다.
📋
fold(초기값, ([매개변수1], [매개변수2]) { return [적용할 동작] });List<String> words = ['다트는 ', '참 ', '재미있군 ?']; var result = words.fold('앗, ', (a, b) { return a + b; });✅ 실행
List<String> words = ['다트는 ', '참 ', '재미있군 ?']; int result = words.fold('앗, ', (a, b) { print('a : $a'); print('b : $b'); print('a + b : ${a + b}'); print('----------------------'); return a + b; }); print('result : $result'); /* a : 앗, b : 다트는 a + b : 앗, 다트는 ---------------------- a : 앗, 다트는 b : 참 a + b : 앗, 다트는 참 ---------------------- a : 앗, 다트는 참 b : 재미있군 ? a + b : 앗, 다트는 참 재미있군 ? ---------------------- result : 앗, 다트는 참 재미있군 ? */
📋
fold(초기값, ([매개변수1], [매개변수2]) => [적용할 동작] );List<String> words = ['다트는 ', '참 ', '재미있군 ?']; var result = words.fold('앗, ', (a, b) => a + b);✔ 매개변수에 적용할 동작을 한줄로 표현 가능한 경우에만 사용할 수 있다.
reduce()fold()초기값 Collection데이터의 첫번째 요소첫번째 매개변수에 넣어준 값 빈 Collection사용 가능 여부X O 타입의 유연성 여부(Collection의 요소 타입과 반환 타입이 달라도 되는지) X O
List<String> words = ['다트는 ', '참 ', '재미있군 ?']; int result = words.fold(0, (a, b) { print('a : $a'); print('b : $b'); print('b length : ${b.length}'); print('---------------'); return a + b.length; }); print('총 글자 수 : $result'); /* a : 0 b : 다트는 b length : 4 --------------- a : 4 b : 참 b length : 2 --------------- a : 6 b : 재미있군 ? b length : 6 --------------- 총 글자 수 : 12 */✅ Collection 타입의 데이터와 다른 타입으로도 반환이 가능하다.
List<int> list = []; var result = list.fold(0, (a, b) => a + b); print(result); // 0✅ Collection 타입의 데이터에 요소가 없어도 초기값이 있기 때문에 오류가 발생하지 않는다.
📁
any()
✔Collection타입의 데이터에 있는 요소 중
하나라도 주어진 조건을 만족하면true를 반환한다.
📋
any(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.any((number) { return number.isEven; }); print(result); // trueList<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.any((number) { return number > 10; }); print(result); // false
📋
any(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.any((n) => n.isEven); print(result); // trueList<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.any((number) => number > 10); print(result); // false
📁
every()
✔Collection타입의 데이터에 있는 모든 요소가 주어진 조건을 만족하면true를 반환한다.
📋
every(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.every((number) { return number > 0; }); print(result); // trueList<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.every((number) { return number.isEven; }); print(result); // false
📋
every(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.any((number) => number > 0); print(result); // trueList<int> numbers = [1, 2, 3, 4, 5]; var result = numbers.every((n) => n.isEven); print(result); // false
📁
takeWhile()
✔Collection타입의 데이터에 있는 요소들을 주어진 조건에 넣었을 때, 참이 되는 동안은 해당 요소들을 반환하고, 조건이 처음으로 거짓이 되는 순간부터의 요소들은 모두 무시한다.
📋
takeWhile(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) { return number < 4; }); print(result); // (1, 2, 3)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) { return number.isOdd; }); print(result); // (1)
📋
takeWhile(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) => number < 4); print(result); // (1, 2, 3)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) => number.isOdd); print(result); // (1)
📁
skipWhile()
✔Collection타입의 데이터에 있는 요소들을 주어진 조건에 넣었을 때, 참이 되는 동안은 해당 요소들을 건너뛰고, 조건이 처음으로 거짓이 되는 순간부터의 요소들을 모두 반환한다.
📋
skipWhile(([매개변수]) { return [조건식] });List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.skipWhile((number) { return number < 4; }); print(result); // (4, 5, 6)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.skipWhile((number) { return number.isOdd; }); print(result); // (2, 3, 4, 5, 6)
📋
skipWhile(([매개변수]) => [조건식] );List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) => number < 4); print(result); // (4, 5, 6)List<int> numbers = [1, 2, 3, 4, 5, 6]; var result = numbers.takeWhile((number) => number.isOdd); print(result); // (2, 3, 4, 5, 6)
📁
map()+where()List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var doubledNumbers = numbers.map((number) => number * 2); var result = doubledNumbers.where((number) => number > 5); print(result); // (6, 8, 10, 12, 14, 16, 18, 20)⬇ 매서드 체이닝
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers .map((number) => number * 2) .where((number) => number > 5); print(result); // (6, 8, 10, 12, 14, 16, 18, 20)
List<String> words = ['apple', 'orange', 'watermelon', 'pineapple']; var longerThanFiveWords = words.where((word) => word.length > 5); var result = longerThanFiveWords.map((word) => word.toUpperCase()); print(result); // (ORANGE, WATERMELON, PINEAPPLE)⬇ 매서드 체이닝
List<String> words = ['apple', 'orange', 'watermelon', 'pineapple']; var upperCasedWords = words .where((word) => word.length > 5) .map((word) => word.toUpperCase()); print(upperCasedWords); // (ORANGE, WATERMELON, PINEAPPLE)
📁
where()+reduce()List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = numbers.where((number) => number.isEven); var result = evenNumbers.reduce((a, b) => a + b); print(result); // 30⬇ 매서드 체이닝
List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var result = numbers .where((number) => number % 2 == 0) .reduce((a, b) => a + b); print(result); // 30
📁
where()+map()+fold()List<int> numbers = [-10, 20, -30, 40, 50]; var positiveNumbers = numbers..where((number) => number > 0); var plusTenNumbers = positiveNumbers.map((number) => number + 10); var result = plusTenNumbers.fold(0, (a, b) => a + b); print(result); // 140⬇ 매서드 체이닝
List<int> numbers = [-10, 20, -30, 40, 50]; var result = numbers .where((number) => number > 0) .map((number) => number + 10) .fold(0, (a, b) => a + b); print(result); // 140
✅ 객체 지향 프로그래밍 (Object-Oriented Programming) 의 핵심 개념이자 기본 단위로, 객체(Object) 의 구조와 동작을 정의하는 틀이다.
📘 객체(Object)
✔ 클래스 에서 정의한 구조를 기반으로 생성된 실제 데이터
📋
class [클래스 이름] { … }class Person { String name; int age; // Person(this.name, this.age); // void introduce() { print('안녕 ? 나는 $age살 $name !'); } }👉 속성 :
name,age
👉 생성자 :Person(this.name, this.age);
👉 메서드 :introduce()
✅ 클래스 안에서 작업을 수행할 때 사용하는 데이터
📁 인스턴스 변수(Instance Variable)
class Person { String name = 'Mini'; int age = 20; }✔ 객체에 속해 있는 변수
🔍
this를 통해 접근 가능하다.
✔ 클래스 내부에서 현재 객체를 참조할 때 사용하며, 객체를 가리키는 키워드라고 보면 된다.
✅ 클래스에서 클래스 안의 속성이나 메서드를 사용할 때
class Person { String name = 'Mini'; int age = 20; // void printName() { print(this.name); } // void printNameAndAge() { this.printName(); print(this.age); } }
✅ 클래스에서 인스턴스 변수와 메서드에 속한 변수를 구분할 때
class Person { String name = 'Mini'; int age = 20; // void changeName(String name) { this.name = name; } }✔
this.name = name;에서
this.name은 인스턴스 변수인name,
name은 매개변수인name을 가리킨다.
✔ this 가 붙지 않은 변수는 더 가까이 있는 변수로 우선 취급된다.
class Person { String name = 'Mini'; int age = 20; // void printName() { print(name); } // void printNameAndAge() { printName(); print(age); } }💡 this는 최대한 덜 쓰는 것이 좋기 때문에, 변수를 구분할 필요가 없다면 굳이 this를 쓰지 않아도 된다.
🔍
클래스의 모든 곳에서 접근할 수 있다.class Person { String name = 'Mini'; int age = 20; // void introduce() { print('안녕 ? 나는 $age살 $name !'); } }✔
Person클래스의 인스턴스 변수인name과age를introduce()메서드 안에서 사용할 수 있다.
🔍
객체가 존재하는 동안 계속 메모리 상에 존재한다.
✔ 메모리 상에 존재한다는 것은 생명 주기, 즉 라이프 사이클을 말한다.
🔍
동일한 클래스로 생성한 객체들이어도 각 객체들은 값을 공유하지 않고, 개별적인 값을 가진다.class Person { String name = ''; int age = 0; } // void main() { Person paul = Person(); paul.name = 'Paul'; paul.age = 25; // Person mark = Person(); mark.name = 'Mark'; mark.age = 30; // paul.age = 27; print(paul.age); // 27 print(mark.age); // 30 }
class Person { final String name = 'Mini'; int age = 25; } // void main() { Person paul = Person(); paul.name = 'Paul'; // 오류 발생 paul.age = 30; }❗ 객체를 통해서 변수의 값을 final 로 지정할 수도 있는데, 이때 값을 변경하면 오류가 발생한다.
📁 지역 변수(Local Variable)
✔ 로컬 변수라고도 하며, 특정 코드 블록 안에 선언된 변수이다.class Person { String name = 'Alice'; // void sayName() { String nameSentence = '내 이름은 $name !'; print(nameSentence); } }🔍
변수가 선언된 코드 블록 안에서만 사용할 수 있다.class Person { String name = 'Mini'; // void sayName() { String nameSentence = '내 이름은 $name !'; print(nameSentence); } // void sayNameAgain() { print(nameSentence); // 오류 발생 } }🔍
변수 가 선언된 코드 블록의 실행이 끝나면 메모리 상에서 사라진다.class Person { String name = 'Mini'; // void sayName() { String nameSentence = '내 이름은 $name !'; print(nameSentence); } }✔
nameSentence는sayName()의 실행이 끝나면 메모리에서 해제되기 때문에 생명 주기가 짧다.
✔ 생명 주기가 짧기 때문에 보통 잠시동안 사용하는 데이터에 사용한다.
📁 정적 변수(Static Variable)
✔ 클래스 변수라고도 하며, 객체에 종속되지 않고, 클래스 자체에 속하는 변수이다.
class Circle { static double pi = 3.14159; }✔
static이 붙은 변수는 정적 변수이다.
🔍
클래스 이름을 통해 접근한다.class Circle { static double pi = 3.14159; double radius = 0; } // void main() { print(Circle.pi); // 3.14159 print(Circle.radius); // Error: Member not found: 'radius'. }
❌
객체를 통해 접근할 수 없다.class Circle { static double pi = 3.14159; double radius = 0; } // void main() { Circle circle = Circle(); print(circle.radius); // 0 print(circle.pi); // 오류 발생 }
❌
this 를 통해 접근할 수 없다.class Circle { static double pi = 3.14159; // void printPi() { print(this.pi); // 오류 발생 } }✔ 객체에 종속되지 않기 때문에
this는 사용할 수 없다.
🔍
객체마다 개별적인 값을 갖지 않고, 모든 객체가 서로 값을 공유한다.class Circle { static double pi = 3.14159; // void printPi() { print(pi); } } // void main() { Circle circle1 = Circle(); circle1.printPi(); // 3.14159 // Circle circle2 = Circle(); circle2.printPi(); // 3.14159 // Circle.pi = 3.14; circle1.printPi(); // 3.14 circle2.printPi(); // 3.14 }❗ 값을 변경하면 Circle 로 만든 객체들의 값이 모두 바뀌기 때문에 다른 객체에 미치는 영향을 고려해야한다.
💡 circle1과 circle2는 각각 다른 객체로 값을 공유할 뿐이다.
인스턴스 변수 로컬 변수 사용 가능 범위 클래스 내의 모든 범위 해당 코드 블록 내의 모든 범위 생명 주기 객체가 존재하는 동안 (객체가 소멸할 때까지) 해당 코드 블록이 실행되는 동안
인스턴스 변수 정적 변수 키워드 static접근 방법 객체를 통해 접근 클래스 이름을 통해 접근 객체끼리 값을 공유하는지 ? X O
✔ 객체의 동작을 정의하는 함수로, 속성을 변경하거나 객체를 가지고 특정 작업을 수행한다.
❕ 매서드는 클래스에 의존하고, 함수는 클래스에 의존하지 않는다.
📁 인스턴스 메서드(Instance Method)
class Person { String name = 'Mini'; int age = 20; // void introduce() { print('안녕 ? 나는 $age살 $name !'); } }✔ 객체에 속해 있는 매서드로,
introduce()가 이에 해당된다.
🔍
this를 통해 접근할 수 있다.class Person { String name = 'Mini'; int age = 20; // void printName() { print(name); } // void printNameAndAge() { this.printName(); print(age); } }✔
this없이printName();도 가능하다.
🔍
클래스의 모든 곳에서 접근할 수 있다.class Person { String name = 'Mini'; int age = 20; // void printName() { print(name); } // void printNameAgain() { printName(); } }
📁 정적 메서드(Static Method)
class Circle { static double pi = 3.14159; // static void printPi() { print('원주율은 $pi !'); } }✔ 클래스 매서드라고도 불리며, 객체에 종속되지 않고, 클래스 자체에 속하는 매서드이다.
✔ 동일하게static을 앞에 붙여준다.
🔍
클래스 이름을 통해 호출한다.class Circle { static double pi = 3.14159; double radius = 0; // static void printPi() { print(pi); } // void printRadius() { print(radius); } } // void main() { Circle.printPi(); // 3.14159 Circle.printRadius(); // 오류 발생 }
🔍
객체를 통해 호출할 수 없다.
🔍this를 통해 호출할 수 없다.
✔ 정적 변수와 동일하게 객체에 종속되지 않고, 클래스 자체에 속하기 때문에객체,this로 호출할 수 없다.
🔍
코드 블록에서 인스턴스 변수를 사용할 수 없다.class Circle { double pi = 3.14159; double radius = 0; // static void printPi() { print(pi); // 오류 발생 } }
class Circle { static double pi = 3.14159; double radius = 0; // void printArea() { print(pi * radius * radius); } }✅ 정적 메서드에서는 정적 변수만 사용 가능하며, 인스턴스 메서드의 코드 블록에서는 정적 변수를 쓸 수 있다.
🔍
객체마다 개별적으로 동작하지 않고, 모두 동일하게 동작한다.
✅ 객체를 생성하고, 초기화하기 위해 사용하는 특수한 메서드
📁 기본 생성자(Default Constructor)
✔ 매개 변수를 갖지 않는 생성자
📋
[클래스 이름]();class Car { Car(); //기본 생성자 생략 가능 }🔍 자동으로 정의되기 때문에 클래스에 따로 명시하지 않아도 된다.
🔍 인스턴스 변수 들이 모두 초기화되어 있는 상태여야 한다.
class Car { String name = ''; List<String> models = []; // Car(); }class Car { String name; // Error: Field 'name' should be initialized because its type 'String' doesn't allow null. List<String> models; // Error: Field 'models' should be initialized because its type 'List<String>' doesn't allow null. // Car(); }❗ 초기값이 없다면 오류가 발생한다.
📁 매개변수 생성자 (Parameterized Constructor)
✔ 매개 변수를 갖는 생성자로, 매개 변수를 통해 외부에 인스턴스 변수들의 초기값을 설정한다.
📋
[클래스 이름](this.변수);class Car { String name; List<String> models; // Car(this.name, this.models); }✔ 초기값이 없어도
Car(this.name, this.models);가 있기 때문에 오류가 나지 않는다.
📋
[클래스 이름]([타입] [매개변수 이름]) : this.변수;class Car { String name; List<String> models; // Car(String name, List<String> models) : this.name = name, this.models = models; }✔
Car(String name, List<String> models)를 통해 받아온name과models가Car의 변수인name과models에 각각 대입된다.
📋
[클래스 이름]([타입] [매개변수 이름]) { this.변수; }class Car { String name = ''; List<String> models = []; // Car(String name, List<String> models) { this.name = name; this.models = models; } }✔
Car(String name, List<String> models)를 통해 받아온name과 models 가 각각Car의 변수인name과models에 대입된다.
class Car { String name; List<String> models; // Car(this.name, this.models); } // void main() { Car car = Car(); // 여기에 값 필수! Error: Too few positional arguments: 2 required, 0 given. }🚨 객체 생성할 때 매개변수 를 넣지 않으면 오류가 발생한다.
📁 네임드 생성자 (Named Constructor)
✔ 클래스 메서드와 같은 형식으로 호출하는 생성자
📋
[클래스 이름].[메서드 이름]([타입] [매개변수 이름]) : this.변수;class Car { String name; List<String> models; // Car.fromList(List values) : this.name = values[0], this.models = values[1]; }
class Car { String name; List<String> models; // Car.fromList(List values) : this.name = values[0], this.models = values[1]; // void speakName() { print('저희는 $name 입니다 !'); } // void speakModels() { print('$models 모델을 가지고 있습니다 !'); } } // void main() { Car car = Car.fromList([ 'BMW', ['320i', '340i', 'M3'] ]); car.speakName(); // 저희는 BMW 입니다 ! car.speakModels(); // [320i, 340i, M3] 모델을 가지고 있습니다 ! }
🚨 클래스에 있는 변수와 타입이 맞지 않는 값을 넣으면 오류가 난다.
📌 특징
🔍클래스와 이름이 같다.
🔍반환값이 없기 때문에 void 타입이다.
🔍클래스를 통해 객체가 생성될 때 자동으로 호출한다.
🔍
생성할 수 있는 객체의 수에는 제한이 없다.class Person { String name; int age; // Person(this.name, this.age); } // void main() { Person paul = Person('Paul', 25); Person mark = Person('Mark', 30); }
🔍
생성한 객체들은 서로 같지 않은 독립된 개체이다.class Person { String name; int age; // Person(this.name, this.age); } // void main() { Person paul1 = Person('Paul', 25); Person paul2 = Person('Paul', 25); print(paul1 == paul2); // false }✔ 같은 값이 들어갔어도 다른 객체로 인식한다.
✅ 하나의 클래스로 여러 객체를 생성할 수 있기 때문에 공통된 속성과 동작을 갖는 코드를 여러 번 작성하지 않아도 돼서 재사용성 높은 코드를 만들 수 있다.
✔ 클래스나 함수에서 데이터 타입을 일반화하여 다양한 타입을 지원할 수 있게 하는 기능으로, 특정 타입에 의존하지 않고, 여러 타입에 대해 동일한 코드를 적용할 수 있도록 한다.
📋
[클래스 이름]<타입 파라미터>class Box<T> { T value; // Box(this.value); // T getValue() { return value; } }✅ 타입 파라미터는 보통 대문자(ex. E, T)로 표현하고, 실제 코드를 실행할 때 타입 파라미터에 실제 데이터 타입을 넣으면 된다.
class Box<T> { T value; // Box(this.value); // T getValue() { return value; } } // void main() { var intBox = Box<int>(10); print(intBox.getValue()); // 10 // var stringBox = Box<String>('Hello'); print(stringBox.getValue()); // Hello }✔
제네릭 함수와 마찬가지로 특정 타입에 의존하지 않고, 여러 타입에 대해 동일한 코드를 적용할 수 있어서 재사용성 높은 코드를 짤 수 있다.
🌱 심화 문법 중 함수와 제네릭 부분은 크게 어렵지 않았지만, 함수형 프로그래밍이 학습량이 많아서 좀 힘들었다.
그래도 매서드 체이닝을 통해 코드를 구성하고, 가독성 좋게 프로그래밍 하는 방법을 배웠다.
형변환 함수의 경우 변환이 안되는 함수만 주의하면 될 것 같았으나, 고차 함수가 그 종류와 양이 많아서 기억을 다 할 수 있을지 모르겠다.
우선, 어떤 함수인지 이해는 했으며, 예제까지도 보면서 결과를 유추할 수 있었다.
다만, 실제 코딩을 할 때 떠올릴 수 있을지는 모르겠으니, 직접 입력해보면서 익히는 수 밖에 없을 것 같다.
그리고 제일 어려웠던? 헷갈렸던 건 클래스를 다루는 객체 지향 프로그래밍인 것 같다.
그래도 제일 헷갈리던 변수와 클래스의 개념을 확실히 잡은 것 같고, 변수나, 매서드, 생성자와 같이 용어가 조금 헷갈릴 뿐이다.
그 특징이라던가 정적 변수, 정적 메서드와 같이 종류들이 좀 많아서 차이점과 같이 특징들이 좀 헷갈렸던 것 같다.
오히려 그 변수 내에서, 메서드 내에서는 헷갈리지 않았는데, 변수와 메서드 이런식으로 묶어서 생각할 때 헷갈렸던 것 같다.
생각보다 함수나 코드? 변수와 같이 형태 등이 많아서 머리가 꽉찬 것 같다.
보면서 과제는 이걸 활용하는 건가 싶기도 하고... 좀 복잡하다.
🚀 내일은 객체 지향과 상속 1강을 듣고, 개인 과제를 시작해야될까 싶다.
그 외에도 3강이 더 남았는데, 과제를 완수하는데 얼마나 걸릴지 모를뿐더러 제출이 금요일 낮인 것을 감안하면 목요일까지 밖에 없다고 봐도 무방할 것 같다.
그리고 아마 강의는 이번주까지 들으면 되니, 금요일에 과제 해설 강의 이후에 나머지 강의를 들으면 되지 않을까..!
진짜.. 오늘은 너무너무.. 힘들었던 것 같다..
어제는 그래도 아는 내용이 대다수라서 개념을 다시 확인한 느낌이라면 오늘은 새로 배우는 것도 많았고 용어 자체가 익숙하지 않아서 머리가 터질 것 같다 💫