코틀린 : 멤버 변수와 getter/setter

허훈·2023년 12월 12일
0

kotlin

목록 보기
1/2
post-thumbnail
class Student(name: String) {
    val studentName: String

    init {
        this.studentName = name
    }

     fun getStudentName() = this.studentName
}

fun main() {
    val student = Student("John")

    println(student.studentName)
    println(student.studentName)
}

위 코드를 실행하니, 다음과 같은 오류가 발생했다.

흠🤔 시그니처가 같은 메서드가 두 개 이상 선언된 것 같은데...
잘 모르겠으니 위 Kotlin 코드를 Java 코드로 변환하여 뜯어보자.

참고로, Kotlin 코드를 Java 코드로 변경하는 방법은 아래를 참고하자.
https://velog.io/@hunzz/intellij-1

엥 나는 Student 클래스에서 하나의 getStudentName() 메서드만 만들었는데,
왜 Java 코드에서는 2개의 getStudentName() 메서드가 만들어진걸까?

public class Student {
    private String studentName;
    private int studentAge;

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public int getStudentAge() {
        return studentAge;
    }

    public void setStudentAge(int studentAge) {
        this.studentAge = studentAge;
    }
}

Java에서는 보통 멤버 변수를 private로 선언하여 외부에서 멤버 변수로의 직접 접근을 막고(= 캡슐화), 그 대신 간접적으로 접근할 수 있도록 getter/setter 메서드를 개발자가 직접 생성해야만 했다. 이 때, (물론 setter를 제공해주는 경우는 흔치 않지만) 대부분의 멤버 변수에 getter/setter를 제공해주다보면, 코드가 길어져 가독성이 떨어지는 큰 문제점이 있다.

class Student() {
    var studentName: String = ""
}

fun main() {
    val student = Student()

    student.studentName = "John" // setter
    println(student.studentName) // getter
}

이를 보완하고자 Kotlin에서는, 클래스 내부에서 멤버 변수를 선언하게 되면 위와 같이 getter/setter를 개발자가 따로 만들어주지 않아도 자동으로 생성해준다.

참고로, val로 멤버 변수를 선언했을 때에는 getter만 자동으로 생성해준다.

class Student(name: String) {
    val studentName: String

    init {
        this.studentName = name
    }

     fun getStudentName() = this.studentName
}

fun main() {
    val student = Student("John")

    println(student.studentName)
    println(student.studentName)
}

아무튼, 맨 처음에 오류가 났던 그 코드로 돌아가보자.
Student 클래스에서 멤버 변수 studentName는 val로 선언되었고, 그렇기 때문에 JVM에서 자동으로 getter를 만들어 줄 것이다. 이 때, 자동으로 만들어지는 getter의 메서드명은 get+[멤버변수명]으로 만들어진다.

이미 getter가 JVM에 의해 생성되었음에도 불구하고, 내가 임의로 getStudentName() 메서드를 또 만들었으니... 시그니처가 같은 메서드가 두 개가 선언된 꼴이 되어 오류가 발생한 것이다!

  • 메서드 시그니처 = 메서드명, 매개변수 개수/순서/타입
    → 하나의 클래스는 같은 시그니처를 가진 메서드를 2개 이상 가질 수 없다.

이를 해결하기 위해서는 어떻게 해야할까?

1) 멤버 변수의 접근 제한자를 private로 설정한다.

class Student(name: String) {
    private val studentName: String

    init {
        this.studentName = name
    }

    fun getStudentName() = this.studentName
}

fun main() {
    val student = Student("John")
    println(student.getStudentName())
}

클래스 내에서 멤버 변수를 private var과 private val로 선언하면 JVM은 getter/setter를 제공해주지 않는다. 따라서 접근 제한자를 private로 선언한 후에 개발자가 임의로 getter/setter를 만들어서 사용하면, 메서드명이 겹칠 일이 없어서 오류가 발생하지 않는다.

2) 메서드명을 변경한다.

class Student(name: String) {
    val studentName: String

    init {
        this.studentName = name
    }

    fun getName() = this.studentName
}

fun main() {
    val student = Student("John")
    println(student.getName())
}

정말 간단한 해결방법이다. JVM이 getStudentName()이라는 getter를 자동으로 생성해주니깐, 나는 getStudentName을 제외한 다른 이름으로 메서드명을 변경하는 것이다. getName()으로 변경해보는 것이 그나마 차선책일 듯 하다.

하지만, 방법 2)는 그다지 권장하는 방법은 아니다.
메서드명은 해당 메서드의 기능을 직관적으로 파악할 수 있어야 하는데, 메서드명을 getName보다는 getStudentName로 선언했을 때 '학생의 이름을 받는 메서드'라는 의미가 더 부각되기 때문이다.

3) @JvmName 어노테이션을 사용한다.

class Student(name: String) {
    val studentName: String

    init {
        this.studentName = name
    }

    @JvmName("getName")
    fun getStudentName() = this.studentName
}

fun main() {
    val student = Student("John")
    println(student.getStudentName())
}

개발자가 임의로 만든 getter 위에 @JvmName 어노테이션을 붙인다. @JvmName 어노테이션이 붙어진 메서드는, Java 바이트코드(.class)로 컴파일될 때 사용자가 정의해놓은 이름으로 컴파일된다.

그런데 이 경우에는 getStudentName 메서드와 getName 메서드가 두 개가 생기게 된다. 큰 영향은 없지만, 굳이 메서드명을 바꿔가면서 쓸데없는 메서드를 하나 더 만들어내는게 불편하다면 아래와 같은 방법을 사용해보자.

4) @JvmField 어노테이션을 사용한다.

class Student(name: String) {
    @JvmField
    val studentName: String

    init {
        this.studentName = name
    }

    fun getStudentName() = this.studentName
}

fun main() {
    val student = Student("John")
    println(student.getStudentName())
}

클래스 내 멤버 변수에 @JvmField 어노테이션을 붙이면, 컴파일 시 JVM이 getter와 setter를 만들어주지 않는다. 따라서 개발자가 임의로 만든 getter 메서드만 만들어지게 되는 것이다.

0개의 댓글