0과 1을 공부하다.

[Kotlin] 코틀린 클래스 본문

Study/Kotlin

[Kotlin] 코틀린 클래스

Developer_Jay 2024. 7. 2. 23:14
728x90

 

접근 제어자

 

자바의 접근제어 (가시성 제어)

  • public : 모든 곳에서 접근 가능
  • protected: 같은 패키지 또는 하위 클래스에서만 접근 가능
  • default: 같은 패키지에서만 접근 가능
  • private: 선언된 클래스 내에서만 접근 가능

 

코틀린의 접근 제어 (가시성 제어)

  • public : 모든 곳에서 접근 가능
  • protected: 선언된 클래스 또는 하위 클래스에서만 접근 가능
  • internal: 같은 모듈에서만 접근 가능 (한 번에 컴파일 되는 코드)
  • private: 선언된 클래스 내에서만 접근 가능

 

코틀린 파일 접근 제어

  • public : 기본값 어디서든 접근할 수 있다.
  • protected: 파일 최상단에서는 사용할 수 없다.
  • internal: 같은 모듈에서만 접근 가능
  • private: 같은 파일 내에서만 접근 가능

클래스의 생성자에 접근 지시자를 붙이려면 constructor을 붙어야 한다.

class Person private constructor () {

}

 

 

클래스 선언 및 인스턴스 생성

 

  • 기본 선언 및 인스턴스 생성
fun main() {
    val jay = Person("jay", 30)
    println("name: " + jay.name)
    println("age: " + jay.age)
}

// 생성자를 통해 멤버 변수를 선언하고 초기화 할 필요 없음
class Person (
    var name: String, // public / private / protected / internal 를 통해 접근제한을 할 수 있다.
    var age: Int,
)

 

  • 생성자 혹은 프로퍼티 정의

: init{} 생성자가 생성되는 시점에 1회 실행되는 블록

: constructorsms{} 생성자 재정의 -> 주 생성자는 반드시 존재해야 한다. → 최종적으로 주생성자를 호출해야 한다. → 바디를 가질 수 있다.

class Person (
    private var name: String,
    public var age: Int,
) {
    var hobby = "" // 생성자 내부 이외의 별도의 프로퍼티
    init { // 초기화 블록
        print("init")
    }
}

 

 

  • data class

: 데이터 보관용 객체를 만들 때 사용하는 특별한 종류의 클래스

fun main() {
    val jay = Person("jay", 30)
    val jay2 = Person("jay", 30)
    
    println(jay == jay2) // calss 정의에서 data 키워드를 통해 객체 내부의 값을 비교하여 판별함.

}

data class Person (
    private var name: String,
    public var age: Int,
)

 

  • Singleton class : 단 하나의 인스턴스만을 갖는 클래스
object Singleton {
    var counter: Int = 0

    fun printCounter() {
        println("Counter: $counter")
    }

    fun incrementCounter() {
        counter++
    }
}

fun main() {
    Singleton.printCounter() // Counter: 0
    Singleton.incrementCounter()
    Singleton.printCounter() // Counter: 1
}

 

  • Enum class

: eum의 특징은 추가적인 클래스를 상속받을 수 없으며 인터페이스는 구현할 수 있으며, 각 코드가 싱글톤이다.

enum class Country (
	private val code: String,
){
	KOREA("KO"),
	AMERICA("US")
	;
}

 

  • getter setter

: 프로퍼티 = 필드 + getter + setter를 의미한다. 기존 자바에서는 getter와 setter를 정의해야 하지만 코틀린은 기본적으로 프로퍼티에 대해 자동으로 getter와 setter를 생성해 주지만, 필요에 따라 사용자 정의 getter와 setter를 만들 수 있다.

fun main() {
    val jay = Person("jay", 30)
  
    println(jay.hobby)
    jay.hobby = "baseball"
    println(jay.hobby)
}

class Person (
    private var name: String,
    public var age: Int,
) {
   var hobby = "football" 
    	// private set // set private
    	set(value) {
        	// field 키워드를 사용하여 backing field에 접근
            field = value.uppercase() // 커스텀(사용자 정의) setter 
      }
    	get() = field // 커스텀(사용자 정의) getter
   
}

 

 

 

static 함수와 변수

 

: 코틀린에서는 static은 존재하지 않는다. 대신 companion object 블럭을 사용하여 구현한다.

class Calculator(){
    companion object {
        const val PI: Double = 3.14159 // const 키워드를 붙으면 런타임시에 할당되는 것이 아닌 컴파일시에 값이 할당됨.
        fun sum(x: Int, y: Int): Int = x + y
        fun circumference(radius: Int): Double = 2 * PI * radius
    }
}

fun main(){
    println(Calculator.PI)
    println(Calculator.sum(1,2))
    println(Calculator.circumference(5))
}

 

 

상속(Extends)

 

상속(Extends)을 설명하기 이전에 abstract 키워드와 open 키워드에 대해 설명한다.

  • abstract:
    • 클래스: 인스턴스화할 수 없고, 반드시 다른 클래스에 의해 상속되어야 한다.
    • 메서드: 구현이 없고, 반드시 하위 클래스에서 구현해야 한다.
  • open:
    • 클래스: 기본적으로 상속할 수 없는 클래스에 대해 상속 가능하게 한다.
    • 메서드: 기본적으로 재정의할 수 없는 메서드에 대해 재정의 가능하게 한다.
    • 추상 프로퍼티가 아니라면 상속받을 때 꼭 open을 붙인다.
    • 프로퍼티를 오버라이딩 할 때는 선언에서 open 키워드를 붙어야 한다.
  • final: overrride를 할 수 없게 한다.
fun main() {
    val dog = Dog()
    val cat = Cat()
  
    println(dog.move()) 
    println(cat.move()) 

}

// 기본적으로 일반 클래스는 상속이 불가능하며, open 키워드를 붙어야 함. abstract = X
abstract class Animal {
    open fun move() { // 'open'은 override 가능 상태로 만든다.
        println("Animal move")
    }
}

class Dog: Animal() // 반드시 생성자 호출 해야함 {
    // Override
    override fun move() {
        println("Dog move")  
    }
}

class Cat: Animal() {
    // Override
    override fun move() {
        println("Cat move") 
    }
}

 

추가로 상위 클래스를 설계할 때 생성자 또는 초기화 블록에 사용되는 프로퍼티에는 open을 피해야 한다.

 

 

인터페이스(Interface)

 

fun main() {
    val dog = Dog()
    val cat = Cat()
  
    println(dog.move()) 
    println(cat.move()) 

}

interface Drawable {
    fun draw()
}

abstract class Animal {
    open fun move() { // 'open'은 override 가능 상태로
        println("Animal move")
    }
}

class Dog: Animal(), Drawable {
    // Override
    override fun move() {
        println("Dog move")  
    }
    override fun draw(){
      println("Dog draw")  
    }
}

class Cat: Animal(), Drawable {
    // Override
    override fun move() {
        println("Cat move") 
    }
    override fun draw(){
      println("Dog draw")  
    }
}

 

 

객체 타입체크 is / 강제 타입 변환 as

 

// 상위 클래스 정의
open class Animal {
    open fun move() {
        println("Animal move")
    }
}

// Dog와 Cat 클래스는 Animal을 상속받음
class Dog : Animal() {
    override fun move() {
        println("Dog move")
    }
}

class Cat : Animal() {
    override fun move() {
        println("Cat move")
    }
}

fun main() {
    val dog: Animal = Dog()
    val cat: Animal = Cat()

    // 생성된 인스턴스의 클래스 비교
    if (dog is Cat) {
        println(dog.move())
    } else {
        println("dog is not a Cat")
    }

    // 타입 변환
    val test = cat as? Dog
    println(test) // Output: null
}

    

 

 

제네릭(Generic)

 

: 타입 안전성을 높이고 코드의 재사용성을 증가시키기 위해 사용되는 기능이다. 클래스, 함수, 인터페이스 등을 정의할 때 타입을 매개변수화하여 다양한 타입의 객체들을 다룰 수 있다.

 

  • 제네릭 클래스
fun main() {
    val boxInt = Box(10)
    val boxString = Box("BOX")

    println(boxInt.value)
    println(boxString.value)
}

class Box<T>(var value: T)

 

  • 제네릭 함수
fun <T>Box(value: T): T
{
    return value
}
fun main() {
    val boxInt: Int = Box(10)
    val boxString: String = Box("BOX")

    println(boxInt)
    println(boxString)
}

 

 

구조분해

 

: 복합적인 값을 분해하여 여러 변수를 한 번에 초기화 하는 것

data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("박지환", 29)
    val (name, age) = person // 프로퍼티 순서에 따라 할당됨.

    println("이름: ${name} | 나이: ${age}")
}

 

 

확장 함수 / 중위 함수

 

확장 함수

: 기존 클래스에 새로운 함수를 추가할 수 있는 기능

  • 확장함수는 클래스에 있는 private 또는 protected 멤버를 가져올 수 없다.
  • 확장함수와 멤버함수의 시그니처가 같으면 멤버함수가 우선적으로 호출된다.
  • 확장함수는 현재 타입을 기준으로 호출된다. 즉, 오버라이딩 되어도 타입을 기준으로 호출한다.
// String 클래스에 확장 함수 추가
fun String.addExclamation(): String {
    return "$this!"
}

fun main() {
    val hello = "Hello"
    val helloWithExclamation = hello.addExclamation()

    println(helloWithExclamation) // "Hello!" 출력
}

 

 

중위 함수(infix)

: .과 ()를 생략하고, 연산자처럼 사용할 수 있는 함수

class Calculator(val x: Int){
    infix fun sum(y: Int) : Int = x + y
}

fun main(){
    val obj = Calculator(1)
    println(obj sum 2)
}

 

 

 

 

 

 

※ 본 게시글의 정보가 잘못 되었거나 부족한 부분에 대한 피드백을 환영합니다.

 

 

* CopyRight 2024. Jay Park All rights reserved.

728x90

'Study > Kotlin' 카테고리의 다른 글

[Kotlin] 코틀린 참고하기  (0) 2024.07.02
[Kotlin] 코틀린 코루틴  (0) 2024.07.02
[Kotlin] 코틀린 함수  (0) 2024.07.02
[Kotlin] 코틀린 조건문 / 반복문  (0) 2024.07.02
[Kotlin] 코틀린 리스트/배열  (0) 2024.07.02
Comments