본문 바로가기
Android/Kotlin

[Android] Kotlin Property (프로퍼티)

by DnaJ 2019. 8. 23.
반응형

Kotlin Property (프로퍼티)

Property 필드접근자한데 묶어부르는 말이다.

예를 들어 java 에서 멤버변수 + getter / setter 를 함께 묶어서 프로퍼티라 부른다.

 

Java에서는 멤버변수를 생성하고 getter / setter를 생성해야 했지만

Kotlin에서는 Property기능을 기본적으로 제공한다.

 

https://play.google.com/store/apps/details?id=com.danchoo.tagalbum&hl=ko

 

태그앨범 - Google Play 앱

사진과 앨범을 태그로 관리하세요. 결혼식, 팬클럽, 동호회등 원하는 카테고리를 만들어 정리해보세요. 사진에 태그를 설정하여 손쉽게 찾아보세요!

play.google.com

 

 

iOS를 하시던 분들은 너무나 익숙한 개념이지만, 기존 Java를 하시던 분들은 조금 생소할 수 있는 개념이다.

일단 아래 코드를 보겠다.

 

PropertySample라는 class에서 property 를 생성하고 프로퍼티를 호출했다.

val 형식과 var 형식을 모두 

 

open class PropertySample {

    public val publicVal: String = "public value"
    protected val protectedVal: String = "protected value"
    private val privateVal: String = "private value"

    public var publicVar: String = "publicVar value"
    protected var protectedVar: String = "protectedVar value"
    private var privateVar: String = "privateVar value"

    open fun callProperty() {
    	Log.d("tag", publicVal)
        Log.d("tag", protectedVal)
        Log.d("tag", privateVal)

        Log.d("tag", publicVar)
        Log.d("tag", protectedVar)
        Log.d("tag", privateVar)
    }
}

class PropertySampleChild: PropertySample() {

    override fun callProperty() {
    	// 상속을 받은 class 이기 때문에 private 객체의 getter를 사용하지 못한다.
        // 변수를 호출하는 것 같지만 getter를 호출하는 것이다.
        Log.d("tag", publicVal)
        Log.d("tag", protectedVal)
        
        Log.d("tag", publicVar)
        Log.d("tag", protectedVar)
    }
}

class PropertyCallSample {

    fun callProperty() {
    	// 외부 class에서 생성 한객체이기 때문에 public 객체의 getter만 접근이 가능하다.
        // 변수를 호출하는 것 같지만 getter를 호출하는 것이다.
        val propertySample = PropertySample()
	Log.d("tag",propertySample.publicVal)

        Log.d("tag",propertySample.publicVar)
    }
}


 

위에 코드를 보면 멤버 변수를 선언하고 호출한 것처럼 보인다.

하지만 Kotlin내부적으로는 gettter호출하고 있다.

 

https://smartstore.naver.com/happysiso

 

해피시소마켓 : 네이버쇼핑 스마트스토어

SISO

smartstore.naver.com

 

 

Java에서는 사용자가 getter / setter를 만들었지만

Kotlin은 Property선언하게 되면 자동으로 getter / setter 와같은 접근자만들어준다.

또한, 접근하는 방식도 달라졌다. 위의 코드처럼 변수를 직접 호출하는 것처럼 getter를 호출할 수있다.

물론 setter도 마찬가지다.

propertySample.publicVar = "value change" 이렇게 하기만 하면 setter가 호출이 된다.

 

변수로 호출했을 뿐인데 getter / setter가 불리는것 자체가 가장 편리한 기능중 하나라고 생각한다.

그렇게 생각하는 이유는 다음과 같다.

 

Java로 getter / setter를 호출하려면 함수로 호출해야 한다.

변수로 호출하게 되면 getter / setter가 호출이 안된다.

여기서 문제가 발생한다.

 

변수로 접근을 하게되면 어디서 접근하고 변경이 되는지 찾기 힘든 경우가 발생한다.

프로젝트에 보면 해당 class의 멤버 변수라고 getter / setter를 사용하지 않는 경우가 많다. (특히 flag....)

 

class의 멤버변수를 직접 접근하여 값을 변경하는 코드도 있고 setter에서 값이 변경되는 코드가 있을경우

setter만 디버깅을 해보는 어이없는 실수를 할경우가 발생한다.

그렇지 않더라도 setter뿐만 아니라 모든 변경점을 찾아서 확인 해야한다.

 

getter / setter만 사용하면 해당 문제의 리스크를 크게 줄일 수 있다.

어디서 변경이 되든 접근을 하든 공통적으로 호출되는 함수가 있기 때문이다.

 

Kotlin은 자동으로 getter / setter를 호출하게 되어있다.

그렇기 때문에 Java와 같은 상황은 절대 발생하지 않는다.

getter / setter 를 오버라이드하여 확인하면 되기 때문에 가장 편리한 기능중 하나라고 생각한다.

 

* public, internal, protected, private를 붙이지 않으면 기본적으로 public 으로 가시성이 설정된다.

public을 붙이면 아래와 같은 경고가 발생한다. 
Redundant visibility modifier less... (⌃F1) 
Inspection info: This inspection reports visibility modifiers which match the default visibility of an element (public for most elements, protected for members that override a protected member).

중복 가시성 수 정기 (lessF1)

검사 정보 :이 검사는 요소의 기본 가시성과 일치하는 가시성 수정 자 (대부분의 요소에 대해 공개, 보호 된 멤버를 재정의하는 멤버에 대해 보호됨)를보고합니다.

 

Kotlin기본 가시성public 이다.

Java에서는 기본 가시성은 package-private 으로 같은 pakage에서만 접근할 수있었다. 

Kotlin에서는 package-private대신 internal으로 같은 모듈안에서 볼수 있도록 제한이 된 변경자가 추가됐다.

 

* open 변경자

open class PropertySample { ... }

class 앞에 open 변경자가 붙어있다.

kotlin에서는 기본적으로 class와 function이 final 으로 선언된다. (상속을 받지 못한다.)

open 이라는 변경자를 붙여줘야 상속이 가능하다.

 

open : 상속 가능

final : 상속 불가능

abstract : 반드시 상속

 

 

getter / setter

기본적으로 모든 멤버변수는 getter / setter를 가진다.

하지만 val으로 선언한 변수는 setter를 사용하지 못한다.

val으로 선언한 변수는 final 속성이기 때문이다.

변수 이름으로 getter / setter에 접근할 수 있다.

 

Custom getter / setter 

val 으로 선언된 변수custom setter갖지 못한다

 

getter

 

    public val publicVal: String = "public value"
    	get() = field

    protected val protectedVal: String
    	get() = "protected value"

    private val privateVal: String = "private value"
    	get() = field + "1111"

 

먼저 val으로 선언한 변수의 getter 사용법을 보자

첫번째로 위와같이 "get () = 사용자 지정 값"  이렇게 사용할수 있다.

field 는 해당 변수를 직접 접근 할수 있는 키워드이다.

 

public 으로 선언한 변수를 호출하여 출력해보면 public value가 출력이 될것이다.

    public val publicVal: String = "public value"
    get() = field

 

변수를 사용하는 것처럼 getter를 호출하면 된다.

제일 위에 있는 코드를 보면 PropertySampleChild class,  callProperty 함수에서 getter를 호출하고 있다.

ex )

Log.d("tag", publicVal)

출력 값 : D/tag: publicValue

 

protected로 선언한 변수는 초기화를 하지 않고 getter에서 값을 셋팅해주고 있다.

    protected val protectedVal: String
    get() = "protected value"

 

getter를 호출하여 출력해보면 protected value가 출력될것이다.

ex )

Log.d( "tag", protectedVal )

출력 값 : D/tag: protected value

 

마지막으로 private로 선언한 변수를 보자.

    private val privateVal: String = "private value"
    get() = field + "1111"

getter에서 field + "1111" 을 해주고 있다.

ex )

Log.d( "tag", protectedVal )

출력 값 : D/tag: private value1111

 

같은 코드를 조금 다르게 getter를 custom 해봤다. 물론 결과는 동일하다.

{ ... } 으로 묶고 있어서 좀더 복잡한 연산을 하기 용의하게 했다.

 

    public val publicVal: String = "public value"
    	get()  {
        	return field
    	}

    protected val protectedVal: String
    	get() {
        	return "protected value"
    	}

    private val privateVal: String = "private value"
    	get() {
        	return field + "1111"
    	}

 

setter

 

    public var publicVar: String = "publicVar value"
        get() = field
        set(value) {
            field = value
        }

 

 

var로 선언한 변수의 getter는 val로 선언한 변수와 동일하다.

setter는 위와같이 설정해준다. 물론 Custom하지 않으려면 getter를 빼도 된다.

 

    public var publicVar: String = "publicVar value"
        set(value) {
            Log.d("setter", value)
            field = value
        }

 

field에 값을 대입해 줘야지 property의 값변경이된다.

getter 에서 설명했듯이 field 는 해당 변수를 직접 접근 할수 있는 키워드이다.

value에 변경된 값이 들어온다.

 

setter에 로그가 들어가있는 변수에 값을 값을 넣었을때  결과를 출력해봤다.

 

입력

Log.d("tag", publicVar)

publicVar = "change value"

Log.d("tag1", publicVar)

 

출력 값 : 

D/tag: publicVar value

D/setter: change value

D/tag1: change value

 

변수에 대입을 하듯값을 넣게 되면 setter호출이 된다.

 

getterpublic 이고 setterprivate를 사용해야 할경우가 분명히 발생한다.

setter 앞private를 넣어주면 setter의 가시성private 로 설정이 된다.

 

    public var publicVar: String = "publicVar value"
        private set(value) {
            Log.d("setter", value)
            field = value
        }

 

 

 

반응형

댓글