Kotlin Class
Basic
class Contact(val id: Int, var email: String = "홍길동@gmail.com") {
fun printId() {
println(id)
}
}
fun main() {
val contact = Contact(1, "mary@gmail.com")
// Calls member function printId()
contact.printId()
// 1
}
Data Class
클래스 내의 값을 쉽게 비교하기 위해서 제공되는 클래스, 앞에 data를 붙여서 명시하며, toString(), equals(), hashCode(), copy() 메소드를 자동으로 생성해준다.
class끼리 ==
을 사용해서 비교를 하면 값이 같은 경우 true를 반환하는 것으로보아 Javascript의 ===
과 같은 의미는 아닌것 같다.
data class User(val name: String, val id: Int)
val user = User("Alex", 1)
val secondUser = User("Alex", 1)
println("user == secondUser: ${user == secondUser}") // true
Data Class와 Jpa
data class는 자동으로 hashcode, equals, toString을 만들어준다.
연관관계가 있는 경우는 OneToMany일 경우 Lazy Loading이 default인데 (연관관계가 참조될 때 initialize됨)
이 경우에 equals를 사용하게 되면 에러가 발생할 수 있다.
그래서 Kotlin의 data class는 Jpa와 그다지 어울리지 않는다.
Interface
fun main() {
val apple = Apple(10)
apple.eat()
}
class Apple (var count: Int): Fruit {
override fun eat() {
count--
println("Eat apple, Remaining: $count")
}
}
interface Fruit {
fun eat()
}
Abstract Class
- 상속은 constructor의 뒤에 : 을 통해서 상속받을 수 있다.
- open 해놓지 않은 메서드는 override 할 수 없다.
- 부모 클래스에서 private으로 선언된 메서드는 상속받은 클래스에서 사용할 수 없다.
fun main() {
val userEntity: UserEntity = UserEntity(1, "홍길동@gmail.com", "1234")
val loginRequest: LoginRequest = GoogleLoginRequest("홍길동", "홍길동@gmail.com", "1234")
println(loginRequest.whoami(userEntity))
}
abstract class LoginRequest(val email: String, val password: String) {
/// 여기서 private 해버리면 상속받은 클래스에서 사용할 수 없다.
fun validate(userEntity: UserEntity): Boolean {
if (userEntity.email == email && userEntity.password == password) {
return true
}
return false
}
open fun whoami(userEntity: UserEntity): String {
return "LoginRequest"
}
}
class GoogleLoginRequest(private val name: String, email: String, password: String) : LoginRequest(email, password) {
override fun whoami(userEntity: UserEntity): String {
if (super.validate(userEntity)) {
return "Google Login Success : ${this.name}"
}
return "Google Login Fail"
}
}
Sealed Class
sealed class는 컴파일러한테 자신의 자식이 어떤게 있는지 알려준다.
그래서 when
키워드를 사용할 때 컴파일러가 어떤 자식에 대해서 처리가 되지 않았는지를 찾아내서 에러를 출력해준다.
fun main() {
val color: Colors = Blue
/// 여기에서 에러 발생 Blue를 처리해주고 있지 않기 때문
when (color) {
is Red -> println("Color is Red")
is Green -> println("Color is Green")
}
}
sealed class Colors
data object Red : Colors()
data object Green : Colors()
data object Blue : Colors()
Companion Object
Kotlin애서 사용하는 static method들은 class를 선언할 때, companion object 블록안에 선언하면 된다.
코틀린 공식문서 📜
클래스 내부의 객체 선언은 companion object
와 같이 표시될 수 있다.
companion object의 멤버는 클래스 이름을 한정자로 사용하여 간단하게 호출할 수 있다.
data class Api<T>(
val result: Result? = null,
@field:Valid
val body: T? = null,
) {
companion object {
fun <T> OK(body: T?): Api<T> {
return Api(
result = Result.ok(),
body = body
)
}
... other codes
}
}
이렇게 선언한 코드를 다음과 같이 Intance.Companion.method()
형태로 사용할 수 있다.
return Api.Companion.OK(res);
이름지정
companion object는 이름을 지정해줄 수도 있고 생략(생략할 경우 클래스.Companion이 기본 이름이 된다.)할 수도 있다.
class Fruit {
companion object Apple {
fun amount() = 10
}
}
fun main() {
Fruit.Apple.amount()
}
JvmStatic
Instance.Companion.method()
와 같은 형태로 사용하게 되면 Java에서 Kotlin으로 Refactoring을 할 때 어려움을 겪게 된다.
이를 방지하기 위해서 @JvmStatic
을 사용하면 Instance.method()
형태로 사용할 수 있다.
JvmStatic : "Java에서 static하게 동작할거야"
라는 표현과 같은 Annotation이다.
data class Api<T>(
val result: Result? = null,
@field:Valid
val body: T? = null,
) {
companion object {
@JvmStatic
fun <T> OK(body: T?): Api<T> {
return Api(
result = Result.ok(),
body = body
)
}
}
}
Companion object 상속
kotlin에서 Slf4j를 사용할 때 interface로 선언해주고 companion object에서 상속받아서 많이 사용한다고 한다.
예를 들어서 아래와 같이 slf4j Logger interface를 만들어주고
import org.slf4j.Logger
import org.slf4j.LoggerFactory
interface Log {
val log: Logger get() = LoggerFactory.getLogger(this.javaClass)
}
logger를 사용해야 하는 클래스의 companion obejct에서 Log를 상속받으면 companion object의 log 라는 변수에 접근할 수 있다.
코틀린은 interface에서 변수를 선언할 수 있다.
import org.delivery.api.common.Log
@Business
class UserOrderBusiness {
companion object: Log
fun main() {
log.info("Hello World!")
}
}
Bytecode
kotlin byte code는 shift 두 번 누르고
show kotlin bytecode
를 통해서 나오는 창에 Decompile
을 눌러서 확인해볼 수 있다.
이 때, Companion이 public static final
로 선언되어 있는 것을 확인할 수 있다.