[Kotlin Tutorial] 클래스, objects, 그리고 인터페이스 #2
참조 : Kotlin in Action
4.3. Compiler-generated methods: Data classes and Class delegation
4.3.1. Universal object methods
-
Java 에서 == 는 primitive 나 object 의 reference 를 비교하는 것.
Kotlin 에서는 == 가 기본비교이다. equals 를 호출해서 비교한다.
그럼 Java 에서의 == 는? === 로 대체된다.
4.3.2. Data classes: autogenerated implementations of universal methods
-
data class Client(val name: String, val postalCode: Int)
data class 로 정의하면, toString, hashCode, equals 를 자동으로 구현해준다.
기준은 primary constructor 에 명시된 property 기준으로 구현된다.
-
immutable 인 경우 data class 는 copy method 도 제공해준다.
이 copy method 는 일부 param 을 바꾸면서 copy 하는게 가능하다.
data class Client(val name: String, val postalCode: Int){
...
}
val bob = Client(“Bob”, 973293)
val postalChangedBob = bob.copy(postalCode = 382555)
4.3.3. Class delegation: using the “by” keyword
-
class DelegatingCollection<T>(innerList: Collection<T> = ArrayList<T>()) : Collection<T> by innerList{ ... }
Collection 은 interface 이고, 이 interface 의 구현은 innerList 의 녀석을 호출하는 식으로 코드가 생긴다.
Decorating pattern 사용할 때 좋다
이제 바꾸고 싶은 구현만 override 하면 by 가 해당 function 은 안 만든다.
4.4. The "object" keyword : Declaring a class and creating an instance, combined
-
object keyword 는 여러 곳에서 쓰이는데 핵심은 이거다.
object keyword 는 class 를 정의하고 instance 를 만드는 것을 동시에 한다.
object 선언은 singleton 을 만든다.
companion object 는 factory method 들과 관련된 method 들을 갖는 동시에 동시에 class instance 가 필요 없는 경우.
object expression 은 Java 의 anonymous inner class 와 동일
4.4.1. Object declarations: singletons made easy
-
object Payroll{
val allEmployees = arrayListOf<Person>()
fun calculateSalary(){
for (person in allEmployees){
...
}
}
}
싱글톤 완성!
호출은 어차피 singleton 이라 class 이름에 대고 바로 호출한다.
Payroll.allEmployees.add(Person(…))
Payroll.calculateSalary()
instance 를 넘길 때는 그냥 Payroll 을 넘겨버리면 된다
-
object keyword 는 class, property, method, initialize block 등의 정의에 모두 쓰일 수 있다.
그러나 constructor 에는 사용할 수 없다. ( primary, secondary 모두 )
-
object 를 Java 에서 쓸때는 INSTANCE 를 붙여주기만 하면 된다.
Payroll.INSTANCE.allEmployees.add(new Person(…))
4.4.2. Companion objects: a place for factory methods and static members
-
Kotlin 은 static member 를 갖지 않는다.
Java 의 static keyword 는 Kotlin 에 없다.
그 대신 Kotlin 은 top-level function(static methods 대체) 과 object declaration(static methods, static fields 대체) 을 갖는다.
-
대부분의 케이스는 top-level function 을 사용해서 Java 의 static function 을 대체하는 것을 추천한다.
그러나 top-level function 은 class 의 내부에 접근할 수 없다.
그런 경우에 이 object declaration 을 class 안에 정의해서 사용하면 된다.
( 보통 private constructor 인 util, factory 클래스의 경우에 이 녀석을 접근하고자 할 때 유용 )
-
companion keyword 로 지정하면 containing class 의 method 와 property 를 이름으로 직접 접근할 수 있다.
class A{
companion object {
fun bar(){
println(“Companion object called”)
}
}
}
A.bar()
-
companion object 는 모든 private member 를 접근할 수 있다.
그래서 private constructor 는 companion object 안에 들어가면 좋다.
factory pattern 도 마찬가지다.
class User private constructor(val nickname: String) {
companion object {
fun newSubscribingUser(email: String) = User(email.substringBefore(‘@‘))
fun newFacebookUser(accountId: Int) = User(getFacebookName(accountId))
}
}
val subscribingUser = User.newSubscribingUser(“bob@gmail.com”)
4.4.3. Companion objects as regular objects
-
companion object 는 일반 object 와 동일하다.
대부분의 case 에 companion object 는 class name 으로 접근하지만 필요한 경우 이름을 줄 수도 있다.
class Person(val name: String){
companion object Loader{ // 이름을 생각하면 “Companion” 이 기본 이름이다, 왜 붙여주냐? Java 에서 접근하기 위함이다
fun fromJSON(jsonText: String) : Person = ...
}
}
val person = Person.Loader.fromJSON(“{name: ‘Dmitry’}”)
-
companion object 는 interface 를 구현할 수 있다.
interface JSONFactory<T>{
fun fromJSON(jsonText: String) : T
}
class Person(val name: String){
companion object : JSONFactory<Person> {
override fun fromJSON(jsonText: String): Person = ...
}
}
fun loadFromJSON<T>(factory: JSONFactory<T>): T {
...
}
loadFromJSON(Person) // Person.COMPANION 이 아닌 Person 으로 던진다
-
Kotlin 에는 static 이 없지만 그래도 static 이 필요한 경우가 생길 수 있다.
이 경우에는 companion object 내부의 해당 function 에 @JvmStatic, 해당 field 에 @JvmField 을 붙여주면 된다.
이 녀석은 Java 코드와 호환을 위해 사용되는 용도이지 language core 가 아니다. 즉 Java 와 Co-work 하면서 불가피한 경우가 아니면 사용하지 말아라.
/* Kotlin */
class C {
companion object {
@JvmStatic fun foo() {}
fun bar() {}
}
}
/* Java */
C.foo(); // works fine
C.bar(); // !!!! error: not a static method !!!
C.Companion.foo(); // static 이지만 instance 로 접근하는 느낌
C.Companion.bar(); // the only way it works
/* Kotlin */
object Obj {
@JvmStatic fun foo() {}
fun bar() {}
}
/* Java */
Obj.foo(); // works fine
Obj.bar(); // error
Obj.INSTANCE.bar(); // works, a call through the singleton instance
Obj.INSTANCE.foo(); // works too
-
companion object 가 있으면 extension 으로 static function 느낌의 것을 추가할 수도 있다.
class Person(val firstName: String, val lastName: String){
companion object{ // empty 한 것이라도 꼭 써줘야 extension 할 수 있다.
}
}
fun Person.Companion.fromJSON(json: String): Person{
...
}
val p = Person.fromJSON(json)
4.4.4. Object expressions: anonymous inner class rephrased
-
window.addMouseListener(
object : MouseAdapter(){
override fun mouseClicked(e: MouseEvent){
// ...
}
override fun mouseEntered(e: MouseEvent){
// ...
}
}
)
val listener = object : MouseAdapter(){
override fun mouseClicked(e: MouseEvent){ … }
override fun mouseEntered(e: MouseEvent){ … }
}
-
anonymous object 는 singleton 이 아니며,
Java 의 anonymous inner class 와는 달리 여러개의 interface 를 한번에 구현할 수 있거나, interface 를 구현하지 않으면서 선언될 수 있다.
-
Java 와 마찬가지로 anonymous inner class 가 생성된 바깥 scope 의 variable 들을 접근할 수 있지만,
Java 와는 다르게 final 로 선언된 것에 국한되지 않는다.
fun countClicks(window: Window){
var clickCount = 0
window.addMouseListener(object: MouseAdapter(){
override fun mouseClicked(e: MouseEvent){
clickCount++
}
}
}
4.5. Summary
-
Kotlin 의 interface 는 Java 의 것과 비슷하다. 그러나 default method 와 property 를 가질 수 있다.
-
모든 선언은 기본적으로 final 이며 public 이다. ( 함수, 클래스 )
-
non-final 로 만들려면 open 으로 마킹해주라.
-
visibility modifier 로 internal 이 있다. 이 녀석은 같은 module 에서만 접근할 수 있다.
-
Nested class 는 기본적으로 inner 가 아니다. 즉 기본적으로는 static 으로 선언된다.
inner 키워드를 붙여주어야 outer class 접근 가능하다 ( non-static 이다. )
-
sealed class 는 선언 안에 정의된 녀석들만 subclass 로 가질 수 있다.
( Kotlin 1.1 부터는 위치가 상관없어지긴 했다 )
-
initializer block 과 secondary constructor 는 class instance 를 만드는데 유연성을 준다.
-
field identifier 를 써서 accessor body 에서 backing field 를 접근할 수 있다.
-
Data class 는 compiler 가 만드는 equals, hashCode, toString, copy(Immutable 인 경우) 그리고 다른 method 들을 제공한다.
-
class delegation 은 boiler-plate 함수선언을 예방해준다. by 키워드를 사용한다.
-
Object 선언은 Kotlin 방식의 singleton class 정의이다.
-
Companion object(package-level function, property) 는 Java 의 static method 와 field 정의를 대체한다.
-
Companion object 는 다른 object 와 마찬가지로 interface 를 구현할 수 있다.
그리고 extension function 과 property 도 가질 수 있다.
-
Object expression 은 Java 의 anonymous inner class 의 대체이다.
그러면서 더 많은 기능을 갖는데, 여러개의 interface 를 implement 할 수 있다던지, final 이 아닌 outer scope 의 변수 수정이라던지~
댓글