본문 바로가기
Kotlin

Kotlin #10 : 내부 클래스, 연산자 230223

by haheehee 2023. 2. 23.

코틀린 내부 클래스

  • 중첩(Nested)클래스
  • 이너(Inner)클래스
종류 역할
정적 클래스
(static class)
static 키워드를 가지며 외부 클래스를 인스턴스화 하지않고 바로 사용 가능한 클래스
(주로 빌더 클래스에 이용)

중첩 클래스(nested class): 객체 생성 없이 사용 가능
멤버 클래스
(member class)
인스턴스 클래스로도 불리며 외부 클래스의 필드나 메서드와 연동하는 내부 클래스

이너 클래스(inner class): 필드나 메서드와 연동하는 내부 클래스로 inner 키워드가 필요
지역 클래스
(local class)
초기화 블록이나 메서드 내의 블록에서만 유효한 클래스

지역 클래스(local class): 클래스의 선언이 블록에 있다면 지역 클래스
익명 클래스
(anonymous class)
이름이 없고 주로 일회용 객체를 인스턴스화하면서 오버라이드 메서드를 구현하는 내부 클래스.
가독성이 떨어지는 단점

익명 객체(anonymous object): 이름이 없고 주로 일회용 객체를 사용하기 위해 object 키워드를 통해 선언

** 코틀린에서는 object키워드가 붙으면 보통 익명클래스..

// 자바의 멤버(이너) 클래스
class A {
	...
	class B { 
		... // 외부 클래스 A의 필드에 접근 가능
	}
}

// 자바의 정적 클래스
class A {
	static class B { // 정적 클래스를 위해 static 
		// 키워드를 사용
		... 
	}
}




// 코틀린의 이너 클래스
class A {
	...
	inner class B { // 자바와 달리 inner 키워드 필요
		... // 외부 클래스 A의 필드에 접근 가능
	}
}

// 정적 클래스처럼 사용한 코틀린의 중첩 클래스
class A {
	class B { // 코틀린에서는 아무 키워드가 없는 클래스는
		// 중첩 클래스이며 정적 클래스처럼 사용
		... // 외부 클래스 A의 프로퍼티, 메서드에
			//접근할 수 없음
	}
}

 

중첩 클래스(Nested class)

- 코틀린에서 중첩 클래스는 기본적으로 정적(static) 클래스처럼 다뤄짐

 


package com.example.pp223

class Outer {
    val ov = 5
    class Nested {
        val nv = 10
        fun greeting() = "[Nested] Hello! $nv"  // 외부의 ov에는 접근 불가
    }
    fun outside() {
        val msg = Nested().greeting()   // Outer객체 생성 없이 중첩 클래스의 메서드 접근
        println("[Outer]: $msg, ${Nested().nv}")    // 중첩 클래스의 프로퍼티 접근
    }
}

fun main() {
    // static처럼 Outer의 객체 생성 없이 Nested 객체를 생성하여 사용 가능
    val output = Outer.Nested().greeting()
    println(output)

    // Outer.outside()      // 에러 Outer 객체 생성 필요
    val outer = Outer()
    outer.outside()
}

중첩 클래스 사용 예제

ov는 인스턴스형

중첩 클래스인 class Nested는 static형

fun outside()는 인스턴스형

 

package com.example.pp224

class Outer {
    class Nested {
        fun accessOuter() { // 컴패니언 객체는 접근할 수 있음
            println(country)
            getSomething()
        }
    }
    companion object {  // 컴패니언 객체는 static처름 접근 가능
        const val country = "Korea"
        fun getSomething() = println("Get Something...")
    }
}

fun main() {
    val output = Outer.Nested().accessOuter()
    println(output)
}

코틀린 중첩 클래스에서 바깥 클래스 접근 예제

바깥 클래스에서 접근하는 방법 : Outer클래스가 컴패니언 객체를 가지고 있을 때 접근 가능

 


이너 클래스

- 특별한 키워드인 inner를 사용해 선언된 내부 클래스

- 이너 클래스는 바깥 클래스의 멤버들에 접근 가능

- 바깥 클래스의 private 멤버도 접근이 가능


package com.example.pp226

class Smartphone(val model: String) {
    private val cpu = "Exynos"

    inner class ExternalStorage(val size: Int) {
        fun getInfo() = "${model}: Installed on $cpu with ${size}Gb"    // 바깥 클래스의 프로퍼티 접근
    }
}

fun main() {
    val mySdcard = Smartphone("S7").ExternalStorage(32)
    println(mySdcard.getInfo())
}

이너 클래스의 바깥 클래스의 멤버 접근 예제

 


지역 클래스

- 특정 메서드의 블록이나 init 블록과 같이 블록 범위에서만 유효한 클래스

- 블록 범위를 벗어나면 더 이상 사용되지 않음


package com.example.pp228

class Smartphone(val model: String) {
    private val cpu = "Exynos"

    inner class ExternalStorage(val size: Int) {
        fun getInfo() = "${model}: Installed on $cpu with ${size}Gb"    // 바깥 클래스의 프로퍼티 접근
    }
    fun powerOn(): String {
        class Led(val color: String) {  // 지역 클래스 선언
            fun blink(): String = "Blinking $color on $model"   // 외부의 프로퍼티는 접근 가능
        }
        val powerStatus = Led("Red")    // 여기에서 지역 클래스가 사용됨
        return powerStatus.blink()
    }   // powerOn() 블록 끝
}

fun main() {
    val mySdcard = Smartphone("S7").ExternalStorage(32)
    println(mySdcard.getInfo())
    val myphone = Smartphone("Note9")
    myphone.ExternalStorage(128)
    println(myphone.powerOn())
}

메서드에 지역 클래스 적용 예제

 


익명 객체

- 자바에서는 익명 이너 클래스라는 것을 제공해 일회성으로 객체를 생성해 사용

- 코틀린에서는 object 키워드를 사용하는 익명 객체로 같은 기능을 수행


package com.example.pp230

interface Switcher {    // 인터페이스 선언
    fun on(): String
}

class Smartphone(val model: String) {
    private val cpu = "Exynos"

    inner class ExternalStorage(val size: Int) {
        fun getInfo() = "${model}: Installed on $cpu with ${size}Gb"    // 바깥 클래스의 프로퍼티 접근
    }
    fun powerOn(): String {
        class Led(val color: String) {  // 지역 클래스 선언
            fun blink(): String = "Blinking $color on $model"   // 외부의 프로퍼티는 접근 가능
        }
        val powerStatus = Led("Red")    // 여기에서 지역 클래스가 사용됨
        val powerSwitch = object : Switcher {    // 익명 객체를 사용해 Switcher의 on()을 구현
            override fun on(): String {
                return powerStatus.blink()
            }
        }   // 익명(object) 객체 블록의 끝
        return powerSwitch.on() // 익명 객체의 메서드 사용
    }   // powerOn() 블록 끝
}

fun main() {
    val mySdcard = Smartphone("S7").ExternalStorage(32)
    println(mySdcard.getInfo())
    val myphone = Smartphone("Note9")
    myphone.ExternalStorage(128)
    println(myphone.powerOn())
}

익명 객체를 위한 인터페이스 예제

 


연산자 오버로딩

- 연산자에 여러 가지 다른 의미의 작동을 부여 

- 코틀린에서는 특정 연산자의 역할을 함수로 정의

- operator 키워드를 사용해 정의

 

  • a+b : a.plus(b)
  • a-b : a.minus(b)
  • a*b : a.times(b)
  • a/b : a.div(b)
  • a%b : a.rem(b)
  • a..b : a.rangeTo(b)

package com.example.pp233

class Point(var x: Int = 0, var y: Int = 10) {
    // plus 함수의 연산자 오버로딩
    operator fun plus(p: Point) : Point {
        return Point(x + p.x, y + p.y)
    }
}

fun main() {
    val p1 = Point(3, -8)
    val p2 = Point(2, 9)

    var point = Point()
    point = p1 + p2     // Point 객체의 + 연산이 가능하게 됨
    println("point = (${point.x}, ${point.y})")
}

연산자 오버로딩 예제


호출 연산자(invoke operator)

- 함수 호출을 돕는데 사용

package com.example.pp235

class Manager {
    operator fun invoke(value: String) {
        println(value)
    }
}

fun main() {
    val manager = Manager()
    manager("Do something for me!") // manager.invoke("...")형태로 호출되며 invoke가 생략되었다.
}

호출연산자 예제


인덱스 연산자(indexed access operator)

- getter/setter를 다루기 위한 대괄호([]) 연산자 제공

  • a[i]    :    a.get(i)
  • a[i, j]   :   a.get(i, j)
  • a[i] = b   :   a.set(i, b)
  • a[i, j] = b   :   a.set(i, j, b)

단항 연산자(부호)

- 먼저 a의 자료형을 결정하고 매개변수 없이 각 연산자에 대한 함수를 호출한 다음 연산된 결과를 반환

  • +a   :   a.unaryPlus()
  • -a    :   a.unaryMinus()
  • !a    :   a.not()
package com.example.pp237

data class Point(val x: Int, val y: Int)

operator fun Point.unaryMinus() = Point(-x, -y)

fun main() {
    val point = Point(10, 20)    
    println(-point) // 단일 연산자에 의해 (-10, -20) 값을 바꿈
}


포함 범위 연산자

- in 연산자는 반복문에 사용해 특정 객체의 반복에 사용/ 포함 여부 판단

  • a in b   : b.contains(a)
  • a !in b   :   !b.contains(a)
if (i in 1..10) { // 1 <= i && i <= 10 와 동일
	println(i)
}
for (i in 1..4) {
	print(i) // "1234" 출력
}

할당 연산자(augmented assignments)

  • a += b   :   a.plusAssign(b)
  • a -= b   :   a.minusAssign(b)
  • a *= b   :   a.timesAssign(b)
  • a /= b   :   a.divAssign(b)
  • a %= b   :   a.remAssign(b)

동등성 연산자(equality and inequality)

  • a == b   ->                a?.equals(b) ?: (b === null)
  • a != b    ->                !(a?.equals(b) ?: (b === null))

비교 연산자(comparison)

  • a > b :       a.compareTo(b) > 0
  • a < b :       a.compareTo(b) < 0
  • a >= b :       a.compareTo(b) >= 0
  • a <= b :       a.compareTo(b) <= 0

** 양이면 1, 음이면 -1, 동등이면 0

댓글