본문 바로가기
프로그래밍 놀이터/Kotlin

[Kotlin Tutorial] 클래스, objects, 그리고 인터페이스 #2

by 돼지왕왕돼지 2017. 8. 14.

 [Kotlin Tutorial] 클래스, objects, 그리고 인터페이스 #2


참조 : Kotlin in Action

@Jvmstatic, accessor body, annonymous inner class, Anonymous Class, anonymous class final variable, anonymous inner class, anonymous object, backing field, boiler-plate, by, by keyword, CLASS, class delegation, class instance, combined, companion keyword, companion object, companion object name, companion object private member, compile 에러, constructor, COPY, copy method, data class, data modifier, decorating pattern, default method, default property, empty companion object, equals, extension, extension function, factory method, field identifier, Final, hashcode, immutable, initialize block, initializer block, inner, inner keyword, instance, Interface, interface 구현 delegate, Internal, java ==, java anonymous inner class, java static method static field, java 접근, jvmfield, jvmstatic, Kotlin, kotlin ==, kotlin ===, kotlin basic, kotlin factory method, kotlin interface, kotlin singleton, kotlin singleton call from java, kotlin static, kotlin static field, kotlin static keyword, kotlin static members, kotlin tutorial, kotlin 강좌, kotlin 기초 강좌, Method, module, Nested Class, object, object declaration, object declarations, object expression, object expressions, object keyword, object singleton, objects, ompiler-generated methods, Open, outer scope, override, package-level function, property, Public, reference 비교, regular object, sealed class, secondary constructor, Singleton, static field, static function, static members, static method, subclass, top-level function, toString, universal methods, universal object methods, visibility modifier, [Kotlin Tutorial] 클래스, 그리고 인터페이스 #2, 여러 개의 interface, 함수 클래스 기본값


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 의 변수 수정이라던지~





댓글0