-
[Kotlin 문법 강의] 3주차: Kotlin 객체 지향 프로그래밍의 기초안드로이드 2024. 3. 6. 16:30
✏️ TIL(Today I Learned)
객체지양 프로그래밍에 대해 이해할 수 있도록 여러 개념들에 대해 가볍게 배웠다. 매소드와 클래스의 설계와 함께 생성자와 객체들을 활용하는 법을 알게 되었다. 클래스 상속과 오버라이딩 그리고 인터페이스와 구현에 관한 기초를 쌓았다.
textRpg게임을 이어서 만들면서 Character 클래스를 객체화해서 여러가지 캐릭터를 만들었다. 인터페이스 또한 구현해보았는데 아래 전체 코드에서 배운 부분을 설명해두었다.
📝 공부한 Kotlin 요약 정리
메소드 설계)
- 특정한 로직을 가지는 소스코드에 별명(이름)을 붙이면, 그것이 메소드이다.
- 로직을 추상화해놓고 상황에 맞게 실행할 수 있다.
- 코드의 재사용성을 높일 수 있다.
// 메소드 구조 fun 메소드이름(변수명:자료형, 변수명:자료형 ....) : 반환자료형 { 소스코드 로직 } // 예시 fun main() { var num1 = readLine()!!.toInt() var num2 = readLine()!!.toInt() // sum이라는 이름의 메소드를 호출! sum(num1, num2) } fun sum(num1:Int, num2:Int) { var result = num1 + num2 println("num1과 num2의 덧셈결과는 ${result}입니다.") }
클래스 설계)
- Object Oriented Programming (OOP)
- 코틀린은 모든것이 클래스형태이므로 객체화할 수 있다.
- 프로그램에서 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체를 만든다.
- 객체들간의 적절한 결합을 통해 유지보수를 쉽게 할 수 있다.
- 5대 키워드: 클래스, 추상화, 캡슐화, 상속, 다향성
// 클래스 구조 class 클래스이름 { 정보1 정보2 행위1 행위2 } // 데이터를 담는 용도로 사용되며, 주로 데이터의 저장과 전달을 위한 목적으로 설계 data class 클래스이름 { 정보1 정보2 } // Comparable 인터페이스를 구현하는 추상 클래스, 관련된 상수의 집합을 정의하는데 사용 enum class 클래스1 { C, JAVA, KOTLIN } enum class 클래스2(val code: Int) { C(10), JAVA(20), KOTLIN(30) } fun main() { println(클래스1.C.toString()) // 출력: C println(클래스2.KOTLIN.code) // 출력: 30 println(클래스2.KOTLIN.name) // 출력: KOTLIN }
생성자의 활용)
- 기본 생성자와 명시적 생성자가 존재한다.
- 기본 생성자는 이전까지 클래스를 만들던 행위와 차이가 없다.
- 명시적 생성자는 주 생성자와 부 생성자로 구분할 수 있다.
- 한 가지의 형태로 클래스를 실체화할때는 주 생성자를 활용
- 여러 형태로 클래스를 실체화할때는 보조 생성자를 활용
// Init (주 생성자) 의 사용 예시 // 클래스 선언부에 생성자를 명시함 class Character(_name:String, _hairColor:String, _height:Double) { var name:String = "" var hairColor:String = "" var height:Double = 0.0 // 매개변수를 직접 넘기지않음 init { println("매개변수없는 생성자 실행 완료!") } fun fireBall() { println("파이어볼!") } fun compositing(device1:String, device2:String): String { var device3 = device1 + device2 println("새로운 무기인 ${device3}입니다") return device3 } } // Constructor (부 생성자)의 사용 예시 class Character { var name:String = "" var hairColor:String = "" var height:Double = 0.0 // 명시적 생성자 (Constructor) // _name, _hairColor, _height와 같이 생성자에 변수를 넘기는 경우에 사용함 constructor(_name:String, _hairColor:String, _height:Double) { println("${_name}을 생성자로 넘겼어요") println("${_hairColor}를 생성자로 넘겼어요") println("${_height}를 생성자로 넘겼어요") } fun fireBall() { println("파이어볼!") } fun compositing(device1:String, device2:String): String { var device3 = device1 + device2 println("새로운 무기인 ${device3}입니다") return device3 } }
객체의 활용)
- 객체란 모든 인스턴스를 포함하는 개념, 클래스 타입으로 선언된것들을 객체(Object)라고 한다.
- 클래스형태로 설계된 객체를 실체화하면 인스턴스가 생기는데, 인스턴스는 메모리 공간을 차지한다.
- 클래스 실체화: 클래스를 사용하여 객체를 생성하는 과정
객체 생성은 "new" 키워드나 클래스의 생성자를 사용하여 수행, 객체 생성 후에는 해당 클래스의 인스턴스가 메모리에 할당되고 초기화된다. - 프로그램은 객체의 위치정보를 변수에 저장해두고, 필요할 때 참조한다.
fun main() { // 불마법사로 객체화 var magicianOne = Character("불마법사", "red", 180.2) println("${magicianOne.name}의 머리색상은 ${magicianOne.hairColor}입니다") magicianOne.fireBall() } class Character { var name:String = "" var hairColor:String = "" var height:Double = 0.0 // 명시적 생성자 (Constructor) // _name, _hairColor, _height와 같이 생성자에 변수를 넘기는 경우에 사용함 constructor(_name:String, _hairColor:String, _height:Double) { println("${_name}을 생성자로 넘겼어요") println("${_hairColor}를 생성자로 넘겼어요") println("${_height}를 생성자로 넘겼어요") name = _name hairColor = _hairColor height = _height } }
상속)
- 공통적인 요소들이 있다면 부모/자식 클래스를 구분해서 상속관계를 만들 수 있다.
- 상속을 통해 코드의 재사용성을 높일 수 있고, 클래스 간의 계층 구조를 형성할 수 있다.
- 다형성을 구현할 수 있고, 클래스의 내용을 변경해야하는경우 부모 클래스만 변경하면 된다.
- 코틀린은 다른 언어들과 달리 생략된 final 키워드로 기본적으로 상속을 막아, 무분별한 상속으로 예상치 못한 흐름을 방지한다.
- 코틀린은 open 키워드를 활용해서 상속 관계를 만들 수 있다.
open class Parent { open fun someMethod() { // 부모 클래스의 메서드 } } class Child : Parent() { override fun someMethod() { // 자식 클래스에서 부모 클래스의 메서드를 오버라이드 } }
오버라이딩)
- 상속받은 부모 클래스의 정보(프로퍼티)나 행위(메소드)를 재설계하는 것을 의미한다.
- 부모 클래스의 메서드를 자식 클래스에서 재정의하여 자식 클래스에 맞게 수정한다.
// 예시 open class Animal { open fun makeSound() { println("The animal makes a sound") } } class Dog : Animal() { override fun makeSound() { println("The dog barks") } } fun main() { val animal: Animal = Dog() animal.makeSound() // 출력: The dog barks }
오버로딩)
- 같은 이름의 메서드를 여러 개 정의하는 것을 의미한다.
- 동일한 이름을 가진 메서드들이 서로 다른 매개변수를 갖고 다른 동작을 수행할 수 있다.
// 예시 class Calculator { // 정수형 매개변수를 받는 메소드 fun add(a: Int, b: Int): Int { return a + b } // 실수형 매개변수를 받는 메소드 fun add(a: Double, b: Double): Double { return a + b } } fun main() { val calculator = Calculator() println(calculator.add(3, 5)) // 출력: 8 println(calculator.add(2.5, 3.5)) // 출력: 6.0 }
인터페이스)
- 인터페이스: 추상 메서드, 추상 프로퍼티, 그리고 일반 메서드와 프로퍼티를 가질 수 있는 추상 형태의 클래스이다.
- 인터페이스는 구현을 가지지 않으며, 인터페이스를 구현하는 클래스에서 인터페이스의 메서드와 프로퍼티를 구현해야한다.
- 특징
- 추상 메서드와 프로퍼티: 추상 메서드와 추상 프로퍼티를 가질 수 있다. 이러한 멤버들은 기본적으로 구현을 가지지 않는다.
- 구현 가능한 메서드와 프로퍼티: 디폴트 구현을 가진 메서드와 프로퍼티를 가질 수 있습니다. 이러한 멤버들은 인터페이스를 구현하는 클래스에서 오버라이드할 수 있다.
- 다중 상속: 클래스는 여러 개의 인터페이스를 구현할 수 있다.
- 타입 지정: 타입으로 사용될 수 있다. 따라서 인터페이스를 구현하는 클래스의 객체를 인터페이스 타입으로 참조할 수 있다.
// 예시 interface Animal { fun makeSound() val color: String } class Dog(override val color: String) : Animal { override fun makeSound() { println("Woof!") } } class Cat(override val color: String) : Animal { override fun makeSound() { println("Meow!") } } fun main() { val dog: Animal = Dog("Brown") val cat: Animal = Cat("Black") dog.makeSound() // 출력: Woof! cat.makeSound() // 출력: Meow! }
🔎 전체 코드
Character 클래스를 상속받은 Wizard 클래스를 작성하면서 attack() 메소드를 오버라이드하여 공격 시 출력되는 문자열을 변경하였다.
open class Character { open fun attack() { println("기본 공격!") } }
class Wizard : Character { var name:String = "" var age:Int = 0 var gender:String = "" var money:Int = 0 var hp:Int = 0 var mp:Int = 0 constructor(_name:String, _age:Int, _gender:String, _money:Int, _hp:Int, _mp:Int) { println("${name}마법사 생성") name = _name age = _age gender = _gender money = _money hp = _hp mp = _mp } override fun attack() { println("에너지 볼!") } fun fireBall() { println("파이어 볼!") } fun teleport(src:Int, dst:Int) { println("${src}에서 ${dst}로 텔레포트!") } }
또한 Slime 클래스는 GreenSlimeSkill 인터페이스를 구현하는데, 이는 Slime 클래스가 poison() 메소드를 구현해야함을 의미한다. poison()를 구현하여 초록색 슬라임인 경우에만 독을 퍼뜨리는 기능을 수행하도록 했다.
interface GreenSlimeSkill { fun poison() }
class Slime : Monster, GreenSlimeSkill { var name:String = "" var color:String = "" var height:Double = 0.0 var hp:Int = 0 var damage:Int = 0 constructor(_name:String, _color:String, _height:Double, _hp:Int, _damage:Int) { name = _name color = _color height = _height hp = _hp damage = _damage } override fun attack() { println("점성 공격!") } fun jumpAttack() { println("점프해서 내려찍기!") } override fun poison() { if(color == "초록") { println("초록 독 퍼뜨리기!") } else { println("일반 슬라임은 사용할 수 없습니다.") } } }
WorldMain.kt에서 fun main()를 run했을 때의 결과는 아래 사진과 같다. "기본 공격!" 이 아닌 "에너지 볼"이 출력된 것을 확인할 수 있다. 인터페이스 또한 잘 구현된 것으로 보인다.
'안드로이드' 카테고리의 다른 글
[Android] 간단한 계산기 (추상화) (0) 2024.03.07 [Kotlin 문법 강의] 4주차: 객체지향 프로그래밍의 심화 (0) 2024.03.06 [Kotlin 문법 강의] 2주차: Kotlin 프로그래밍의 기초 (0) 2024.03.05 [Kotlin 문법 강의] 1주차: Kotlin을 시작하기 전에 알아야할 내용 (0) 2024.03.05 [Android 개발 종합반] 3주차 - Mbti 테스트 (뷰페이저 ViewPager2) (0) 2024.02.28