어플리케이션, 앱 (Application)/안드로이드 (Android)
안드로이드 jetpack compose 공부 정리 3일차 (코틀린, 함수와 클래스)
sobal
2025. 5. 29. 21:43
함수와 클래스
3일차는 함수와 클래스에 대한 내용이다.
1. 함수 (Function)
함수는 특정 작업을 수행하는 코드 블록이다.
- fun 키워드를 사용하여 정의한다.
- 함수명, 변수명 규칙이 동일하다. (camelCase 권장).
- main() 함수는 프로그램의 시작점이다.
1.1. 함수 정의
fun 함수이름(파라미터1: 타입, 파라미터2: 타입, ...): 반환타입 {
// 함수 내용
return 반환값 // 반환타입이 있을 시
}
// 반환값이 없으면 (Unit) 반환 타입 생략 가능
fun printMessage(message: String) { // : Unit이 원래 있지만 생략
println(message)
}
1.2. 함수 호출
함수이름(인자1, 인자2, ...)
1.3. 함수 예제
fun greet(name: String): String {
return "Hello, $name!"
}
fun main() {
val message = greet("Kotlin")
println(message) // Output: Hello, Kotlin!
printMessage("Just a message.") // Output: Just a message.
}
1.4. 단일 표현식 함수 (Single-Expression Function)
함수 본문이 단일 표현식일 경우 아래와 같이 간단하게 표현할 수 있다.
// 일반 함수
fun double(x: Int): Int {
return x * 2
}
// 단일 표현식 함수 (반환 타입 추론 가능)
fun double(x: Int) = x * 2
// 반환 타입 명시도 가능
fun double(x: Int): Int = x * 2
1.5. 파라미터 (Parameters, 매개변수)
- 함수에 전달되는 입력값이다.
- 각 파라미터는 이름과 타입이 필요하다.
- 파라미터 목록은 괄호 안에 쉼표로 구분하여 나열한다.
- 예시: fun power(base: Int, exponent: Int): Int
기본 파라미터 (Default Parameters)
fun greet(name: String, greeting: String = "Hello") {
println("$greeting, $name!")
}
fun main() {
greet("Amy") // Output: Hello, Amy!
greet("Bob", "Hi") // Output: Hi, Bob!
}
명명된 인자 (Named Arguments)
fun createUser(name: String, age: Int, email: String) {
println("이름: $name, 나이: $age, 이메일: $email")
}
fun main() {
// 순서 상관없이 매개변수 이름으로 호출 가능
createUser(email = "amy@example.com", name = "Amy", age = 25)
}
1.6. 반환 타입 (Return Type)
- 함수가 반환하는 값의 타입이다.
- 반환 타입은 함수명 뒤에 콜론(:)과 함께 명시한다.
- 반환할 것이 없으면 Unit이고 보통 생략한다. (위 printMessage 함수 참고)
- 예시: fun average(numbers: List<Double>): Double
1.7. 함수 오버로딩 (Overloading)
같은 이름의 함수를 여러 개 정의할 수 있다. (단, 파라미터 타입이나 개수가 달라야 함) 컴파일러는 호출 시 인자의 타입이나 개수를 기반으로 적절한 함수를 선택한다.
fun add(a: Int, b: Int): Int {
println("Int 덧셈 호출됨")
return a + b
}
fun add(a: Double, b: Double): Double {
println("Double 덧셈 호출됨")
return a + b
}
fun main() {
println(add(5, 3)) // Output: Int 덧셈 호출됨 \n 8
println(add(3.14, 2.71)) // Output: Double 덧셈 호출됨 \n 5.85
}
1.8. 함수 예제 2 (파라미터, 반환 타입 활용)
fun makeCoffee(sugarCount: Int, name: String): String {
return "$name 커피, 설탕 $sugarCount 스푼."
}
fun calculateArea(width: Double, height: Double): Double {
return width * height
}
fun main() {
val order = makeCoffee(2, "모카")
println(order) // Output: 모카 커피, 설탕 2 스푼.
val area = calculateArea(5.0, 3.0)
println("면적: $area") // Output: 면적: 15.0
}
2. 클래스 (Class) 🏗️
객체(Object)를 생성하기 위한 설계도이다. 클래스는 속성(Properties)과 기능(Methods)을 가진다.
2.1. 클래스 정의
// 주 생성자 파라미터에 val/var 사용 시 바로 프로퍼티(속성)로 선언됨
class 클래스이름(val 속성1: 타입, var 속성2: 타입, ...) {
// 멤버 (추가 속성, 함수, 초기화 블록 등)
}
2.2. 객체 생성 (인스턴스화)
val 객체이름 = 클래스이름(인자1, 인자2, ...)
2.3. 클래스 예제
class Dog(val name: String, val breed: String, var age: Int = 0) { // 주 생성자 + 프로퍼티
// 초기화 블록 (객체 생성 시 가장 먼저 실행)
init {
println("$name ($breed) 등장!")
}
// 멤버 함수 (Methods)
fun bark() {
println("$name: 멍멍!")
}
fun eat(food: String) {
println("$name이(가) $food을(를) 먹는 중입니다.")
}
// 멤버 프로퍼티 (Properties)
var weight: Double = 0.0
// Custom Getter
get() {
// println("$name 몸무게 확인: $field kg") // 필요시 로그 추가
return field // field: 실제 프로퍼티 값을 가리키는 백킹 필드
}
// Custom Setter
set(value) {
if (value >= 0) {
// println("$name 몸무게 ${value}kg 설정 시도.") // 필要시 로그 추가
field = value
} else {
println("몸무게는 음수가 될 수 없습니다 ($value).")
}
}
}
fun main() {
val daisy = Dog("데이지", "푸들", 1) // init 블록 실행
// Output: 데이지 (푸들) 등장!
println("${daisy.name}는 ${daisy.breed}이며 ${daisy.age}살입니다.")
// Output: 데이지는 푸들이며 1살입니다.
daisy.bark() // Output: 데이지: 멍멍!
daisy.eat("사료") // Output: 데이지이(가) 사료을(를) 먹는 중입니다.
daisy.weight = 10.5 // Setter 호출
println("${daisy.name} 몸무게: ${daisy.weight} kg.") // Getter 호출
// Output: 데이지 몸무게: 10.5 kg.
daisy.weight = -1.0 // Setter 호출 (음수 값)
// Output: 몸무게는 음수가 될 수 없습니다 (-1.0).
println("${daisy.name} 몸무게: ${daisy.weight} kg.") // Getter 호출 (값 변경 안됨)
// Output: 데이지 몸무게: 10.5 kg.
}
주의: 프로퍼티의 getter/setter 내부에서 프로퍼티 자신을 직접 참조하면 무한 루프가 발생한다. (예: get() = weight). 반드시 field 키워드를 사용해야 한다.
2.4. 생성자 (Constructor)
클래스의 객체를 생성할 때 호출되는 특수한 함수. 주 생성자(Primary Constructor)와 부 생성자(Secondary Constructor)가 있다.
1. 주 생성자 (Primary Constructor)
- 클래스명 뒤 괄호 안에 정의한다.
- 클래스 정의 시 단 하나만 가질 수 있다.
- init 블록을 사용하여 주 생성자의 초기화 작업을 수행한다.
class Person(val name: String, var age: Int) {
init { // 주 생성자 로직은 init 블록 안에
if (age < 0) {
throw IllegalArgumentException("나이는 음수가 될 수 없습니다.")
}
println("$name ($age 살) 생성됨.")
}
}
fun main() {
val john = Person("John", 30)
// Output: John (30 살) 생성됨.
println(john.name) // Output: John
println(john.age) // Output: 30
// val invalidPerson = Person("Jane", -1) // 예외 발생: 나이는 음수가 될 수 없습니다.
}
2. 부 생성자 (Secondary Constructor)
- 클래스 내부에 constructor 키워드로 정의한다.
- 여러 개 가질 수 있다.
- 부 생성자는 다른 생성자(주 또는 다른 부 생성자)를 호출해야 한다 (this() 사용).
class Book(val title: String, val author: String) { // 주 생성자
var price: Int = 0
// 부 생성자: 주 생성자를 반드시 호출해야 함 (this(title, author))
constructor(title: String, author: String, price: Int) : this(title, author) {
this.price = price // 부 생성자 고유 로직
}
}
fun main() {
val book1 = Book("반지의 제왕", "J.R.R. 톨킨")
println("${book1.title} - 가격: ${book1.price}")
// Output: 반지의 제왕 - 가격: 0
val book2 = Book("오만과 편견", "제인 오스틴", 15000)
println("${book2.title} - 가격: ${book2.price}")
// Output: 오만과 편견 - 가격: 15000
}
3. 주 생성자와 부 생성자 함께 사용
class Car(val model: String, val year: Int) { // 주 생성자
var color: String = "Unknown" // 프로퍼티 기본값
// 부 생성자: 주 생성자 호출 후 추가 작업
constructor(model: String, year: Int, color: String) : this(model, year) {
this.color = color
}
}
fun main() {
val car1 = Car("테슬라 모델S", 2022)
println("${car1.model} (${car1.year}) 색상: ${car1.color}")
// Output: 테슬라 모델S (2022) 색상: Unknown
val car2 = Car("현대 소나타", 2023, "Silver")
println("${car2.model} (${car2.year}) 색상: ${car2.color}")
// Output: 현대 소나타 (2023) 색상: Silver
}
4. 기본 생성자 (Default Constructor)
클래스에 생성자를 명시적으로 정의하지 않으면 컴파일러가 자동으로 생성한다. 매개변수가 없는 형태.
class Mouse { // 생성자 없음 -> 기본 생성자 자동 추가됨
fun click() = println("마우스 클릭!")
}
fun main() {
val myMouse = Mouse() // 기본 생성자 호출
myMouse.click() // Output: 마우스 클릭!
}
5. private 생성자
클래스 외부에서의 객체 생성을 방지하기 위해 사용한다. 주로 싱글톤(Singleton) 패턴 구현 시 활용한다.
class Database private constructor() { // private 생성자
init {
println("데이터베이스 인스턴스가 생성되었습니다.")
}
companion object { // 동반 객체 (자바의 static 멤버와 유사)
private var instance: Database? = null
fun getInstance(): Database {
if (instance == null) {
instance = Database() // 클래스 내부에서는 private 생성자 호출 가능
}
return instance!!
}
}
fun connect() = println("데이터베이스에 연결됩니다.")
}
fun main() {
val db1 = Database.getInstance() // Output: 데이터베이스 인스턴스가 생성되었습니다.
db1.connect() // Output: 데이터베이스에 연결됩니다.
val db2 = Database.getInstance() // 이미 생성된 인스턴스 반환
db2.connect() // Output: 데이터베이스에 연결됩니다.
// val db = Database() // 에러: private 생성자는 외부에서 호출 불가
println(db1 === db2) // Output: true (같은 인스턴스), 참조 동등성 확인
}
다양한 생성자를 활용하여 유연한 클래스 설계가 가능하다.
2.5. 멤버 (Members)
클래스 내부에 정의된 속성(프로퍼티), 함수(메서드), 초기화 블록 등을 말한다.
2.6. 프로퍼티 (Properties)
- 클래스의 속성을 의미한다.
- val(읽기 전용) 또는 var(읽기/쓰기)로 선언한다.
- 프로퍼티는 getter/setter를 가진다 (자동 생성).
- 필요시 getter/setter를 직접 정의할 수 있다. (위 Dog 클래스의 weight 프로퍼티 참고)
3. 데이터 클래스 (Data Class)
데이터 저장/보관 목적의 클래스이다.
- data 키워드를 사용하여 정의한다.
- equals(), hashCode(), toString(), copy(), componentN() 등이 자동으로 생성된다. (보일러플레이트 코드 감소)
3.1. 데이터 클래스 정의
data class 데이터클래스이름(val 속성1: 타입, val 속성2: 타입, ...)
3.2. 데이터 클래스 예제
data class CoffeeDetails(
val sugarCount: Int,
val name: String,
val size: String,
val creamAmount: Int
)
fun main() {
val coffeeForDenis = CoffeeDetails(0, "Denis", "XXL", 1)
println(coffeeForDenis) // toString() 자동 호출
// Output: CoffeeDetails(sugarCount=0, name=Denis, size=XXL, creamAmount=1)
// 데이터 클래스 객체 복사 (copy() - 일부 값 변경 가능)
val coffeeForJohn = coffeeForDenis.copy(name = "John", sugarCount = 2)
println(coffeeForJohn)
// Output: CoffeeDetails(sugarCount=2, name=John, size=XXL, creamAmount=1)
// 데이터 클래스 객체 분해 (Destructuring Declarations - componentN() 함수들 사용)
val (sugar, name, size, cream) = coffeeForDenis
println("$name 커피: 설탕 $sugar, 사이즈 $size, 크림 $cream")
// Output: Denis 커피: 설탕 0, 사이즈 XXL, 크림 1
makeCoffeeWithDetails(coffeeForDenis)
// Output: XXL Denis 커피를 만드는 중: 설탕 0, 크림 1
}
fun makeCoffeeWithDetails(coffeeDetails: CoffeeDetails) {
println("${coffeeDetails.size} ${coffeeDetails.name} 커피를 만드는 중: 설탕 ${coffeeDetails.sugarCount}, 크림 ${coffeeDetails.creamAmount}")
}
3.3. 데이터 클래스의 자동 생성 함수들
data class User(val name: String, val age: Int)
fun main() {
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
val user3 = User("Bob", 30)
// equals() - 내용 비교
println(user1 == user2) // Output: true
println(user1 == user3) // Output: false
// hashCode() - 해시 코드 생성
println(user1.hashCode()) // 동일한 데이터는 동일한 해시 코드
println(user2.hashCode()) // user1과 같은 값
// toString() - 문자열 변환
println(user1.toString()) // Output: User(name=Alice, age=25)
// copy() - 객체 복사
val user4 = user1.copy(age = 26)
println(user4) // Output: User(name=Alice, age=26)
// componentN() - 구조 분해
val (name, age) = user1
println("이름: $name, 나이: $age") // Output: 이름: Alice, 나이: 25
}
4. 참고 자료
- Android Developers 공식 문서: https://developer.android.com/courses
- Jetpack Compose 공식 문서: https://developer.android.com/compose
- Kotlin 공식 문서: https://kotlinlang.org/docs/
You will be redirected shortly
kotlinlang.org