글 작성자: beaniejoy
  • Kotlin에서 주생성자
  • 인터페이스 구현시 getter override 중복 상황
  • 정리

 

📌 1. Kotlin에서 주생성자

코틀린은 자바와 다르게 주생성자, 부생성자라는 개념이 있습니다. 

class User(val nickname: String)

코틀린으로 User 클래스를 만들었습니다. 해당 클래스의 프로퍼티는 nickname 하나뿐입니다.
위 코드처럼 코틀린에서 클래스 이름 뒤에 오는 괄호로 둘러싸인 코드를 주생성자(primary constructor)라고 말합니다.

User 클래스를 코틀린에서 자바 코드로 변환을 하면 다음과 같이 나옵니다.

public final class User {
    private final String nickname;
    
    public final String getNickname() {...}
    
    public User(String nickname) {...}
}

코틀린에서 주생성자로 val nickname을 정의하면 자바에서는 그에 대한 필드와 getter, 해당 필드에 대한 생성자 코드를 만들어줍니다.
(코틀린은 기본적으로 클래스와 메소드가 final이라서 자바 코드로 변환했을 때 access modifier 뒤에 final이 붙는 것을 볼 수 있습니다.)

여기서 코틀린의 장점이 나옵니다. 자바에서 필드, getter(var는 추가로 setter까지), 생성자를 따로 구성을 했던 것을 코틀린에서는 한 줄로 커버가 됩니다.

이번 게시글에서는 주생성자에 대한 내용은 이 정도로 적당하기 때문에 다음 내용으로 넘어가도록 하겠습니다. 주생성자, 부생성자에 대한 자세한 내용은 따로 게시글을 작성해보도록 하겠습니다. (혹은 구글링~)

 

📌 2. 인터페이스 구현시 getter override 중복 상황

상황을 하나 가정해야 될 듯 싶습니다.
Spring Security에서 제공하는 인증 처리를 적용하기 위해 UserDetails를 사용하게 되는데요. 커스터마이징을 위해 UserDetails 인터페이스를 구현해 새로운 구현 클래스를 정의하려고 하는 상황입니다.

public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
    String getPassword();
    String getUsername();
    
    //...
}

UserDetails 인터페이스는 위에서 볼 수 있듯이 해당 인터페이스를 구현하는 클래스는 getUsername, getPassword, getAuthorities 같은 getter들을 구현해야 합니다. 코틀린에서 UserDetails를 구현을 해보았습니다.

 

class SecurityUser(
    memberId: Long,
    username: String,
    password: String,
    authorities: Collection<GrantedAuthority>
) : UserDetails {
    val memberId: Long
    private val username: String
    private val password: String
    private val authorities: Collection<GrantedAuthority>

    init {
        this.memberId = memberId
        this.username = username
        this.password = password
        this.authorities = authorities
    }
    
    // overriding method
 }

username, password, authorities에서 추가로 저장하고 싶은 memberId 까지 필드로 정하고 init block을 통해 주생성자에서 프로퍼티 초기화를 진행해야 합니다.

여기서 memberId를 제외한 나머지 세 개의 프로퍼티에 대해서 접근 제한자를 private으로 설정했습니다. private 프로퍼티는 따로 자바 코드에서 getter를 생성하지 않습니다. username, password, authorities 프로퍼티에 대한 getter는 UserDetails에서 overriding 받아서 구현해야 하기 때문에 private으로 지정해야 합니다.
(private 없이 프로퍼티를 설정하면 컴파일 에러 발생합니다. private 없는 프로퍼티에 대해서 public final getter가 생성되기 때문에 UserDetails 인터페이스의 getUsername() 메소드를 구현할 수 없게 됩니다.)

위 코드를 주생성자로 처리하면 더 간결하게 작성할 수 있습니다.

class SecurityUser(
    val memberId: Long,
    private val username: String,
    private val password: String,
    private val authorities: Collection<GrantedAuthority>
) : UserDetails {
    // overriding methods
}

이런 식으로 주생성자에 프로퍼티를 설정하면 init 블록을 사용해서 초기화하지 않아도 알아서 생성자를 통해 초기화가 이루어집니다.

 

📌 3. 정리

  • 코틀린에서 주생성자는 자바에서 번거롭게 생성해야하는 클래스 구성 코드들을 한 줄로 커버할 수 있게 해준다.
  • 필드 접근 제한을 private으로 설정하면 자바 코드에서는 따로 해당 필드에 대한 getter 메소드를 제공하지 않는다.
    - private 없는 val usernamepublic final String getUsername() 을 제공해준다.