규턴의 개발블로그
article thumbnail

현재 들어간 회사에서는 코프링이 주 핵심 기술스택이기에 해당 기술스택을 배우고자 코틀린 인 액션 스터디를 진행한다.

  • 아래의 내용은 코틀린 인 액션을 저의 방식으로 정리한 글입니다.

1. 인터페이스

  • 코틀린에서의 override는 자바와 달리 @Override annotaion을 사용하지 않음
  • 인터페이스에 프로퍼티 선언이 가능하다.
  • 자바와 동일하게 하나의 클래스에 대해서만 extends가 가능하며, 반대로 여러개의 인터페이스 가능
  • 코틀린에서는 ":" 으로 상속, 인터페이스화  둘다 가능하다
  • kotlin에서의 default 메서드 구현은 자바와 좀 다르다 
    • 추가로 아래의 코드와 같이 두개의 같은 default method를 상속한다면, 반드시 override가 필수적이다
<bash />
interface Clickable { fun click() fun showOff() = println("default method") }
<bash />
interface Clickable { fun click() fun showOff() = println("default method") } interface Focusable { fun showOff() = println("default method2") } class Button : Clickable, Focusable{ override fun click() = println(" i was clicked") override fun showOff() { //동일한 메서드를 가진 interface를 2개 인터페이스화 하는경우 override 필수 super<Clickable>.showOff() //super<~> 를 통해 상위 클래스 지정 가능 super<Focusable>.showOff() } }

2. 클래스의 상속

  •  기본적으로 코틀린에서 클래스는 final이 붙는다
    • 즉, 일반적으로 상속을 하게되면 에러가 날 수 있다. (반대로 자바는 상속에 기본적으로 열려 있음)
    • 해당 컴파일에러를 고치기 위해선 상위 클래스에서 'open' 변경자를 붙어야 한다.
    • 하지만 추상클래스에서는 'open' 변경자가 필요없다. 추상클래스 자체로는 인스턴스화가 불가능하기 때문
<bash />
abstract class Animated { abstract fun animate() // 추상함수 open fun stopAnimating(){} // 비추상함수이지만 open으로 override 가능 fun animateTwice(){} // override 불가능(비추상함수) }

2.1. 변경자 표

변경자 설명
final 오버라이드 불가능, 기본변경자
open 오버라이드 가능, open이 있어야 override가능
abstract 반드시 오버라이드 해야함
override 오바리이드는 기본적으로 열려 있음, 원한다면 final로 금지를 시켜야함 

3. 내부 클래스, 중첩클래스 => 기본적으로 중첩 클래스

  • 자바에서는 중첩 클래스를 작성할때 static class A 와 같이 static이 붙게 된다, 반대로 코틀린은 class A로 가능하다.
  • 코틀린에서 일반적으로 중첩클래스는 Class A, 외부 클래스에 참조를 하고자 하는경우에는 inner class A로 지정한다.
  • 코틀린에서 outer class에 접근하려면 this@를 붙어야 한다.
클래스 B안에 정의된 A클래스 자바 코틀린
중첩클래스(바깥쪽 클래스에 대한 참조를 저장하지 않음) static class A class A
내부클래스(바깥쪽 클래스에 대한 참조를 저장) class A inner Class A
<bash />
//kotlin class Outer{ inner class Inner{ fun test():Outer= this@Outer } } //java public class OuterJava { class Inner{ public OuterJava getOuterReference(){ return OuterJava.this; } } }

4. Sealed Class

<bash />
sealed class Expr { class Num(val value:Int) : Expr() class Sum(val left:Expr, val right:Expr): Expr() } fun eval(e: Expr): Int = when (e) { is Expr.Num -> e.value is Expr.Sum -> eval(e.left) + eval(e.right) // else-> throw Exception() 필요없음 }

5. 뻔하지 않은 생성자와 프로퍼티를 갖는 클래스 선언

  • 클래스 생성자는 아래의 코드와 같이 생성된다.
<bash />
Class User(val nickname:String , val tf : Boolean = true //디폴트 파라미터도 가능) or Class User(\_nickname:String){ val nickname: String init{ nickname = \_nickname } }
  • init 블록은 객체가 만들어질때(인스턴스화 될떄) init블럭내의 코드가 실행됨.
  • 첫번째 방법과 같이 작성한다면 컴파일러가 해당 파라미터에 맞게 생성자를 만들어준다. ( 내가 듣기론 이러한 이유 때문에 kotlin에서는 빌더 패턴이 필요없다고 들었다.)
  • Class User(val nickname:String)에서 val은 해당 파라미터에 상응하는 프로퍼티가 생성된다는 의미이다.
  • 참고로 ' class SecretClass prviate constructor(){}' 와 같은 클래스를 지정하면 해당 생성자만 비공개(private)지정할 수 있다. 이는 추후 유틸함수나 companion object에 해당 기능을 사용하면 좋을 수 있다.(이유는 이후 장에 나옴)

6. 인터페이스에 선언된 프로퍼티 구현

<bash />
interface User { val nickname:String } class PrivateUser(override val nickname: String) :User { } class SubscribingUser(val email: String):User { override val nickname: String get() = email.substringBefore("@") } class FackebookUser(val accountId:Int):User{ override val nickname=getFacebookName(accountId) }
  • 인터페이스에도 프로퍼트 선언이 가능하다
  • 클래스에서 User인터페이스를 상속하려면  인터페이스의 프로퍼티 때문에 override가 필요하다
  • 커스텀 getter를 사용해서도 가능하다.(PrivateUser 참고)
    • 커스텀getter같은 경우는 nickname을 호출할때 마다 customgetter의 로직을 수행하여 Nickname을 가져온다.
    • 반대로 FacebookUser의 nickname같은 경우는 처음 객체가 지정될때 nickname을 저장하고 이후 다음에 nickname을 호출한다면, 이미 저장된 nickname을 호출한다.

 

7. 모든 클래스가 정의해야 하는 메서드

<bash />
class Client(val name: String, val postalCode: Int) { override fun toString(): String { return "Cleint(name=$name, postalCode=$postalCode)" } override fun equals(other: Any?): Boolean { if(other == null || other !is Client) return false return name==other.name && postalCode==other.postalCode } override fun hashCode(): Int { return name.hashCode()*31+postalCode } }
  • 코틀린에서 equals메서드는 자동으로 '=='비교를 할 시에 객체간의 비교인경우 equals가 호출된다
  • 코틀린에서의 is는 자바에서의 instanceof와 같은 역할을 한다. ( a is User == a instanceof User)
  • 원래는 JVMd에서 equals만 정의해서는 의도하지 않는 false값이 나올 수 있다. 실제로는 hashCode도 구현을 해줘야 한다. 하지만 코틀린 컴파일러(자바도 되는지는 정확하지 않음) 해당 메서드를 자동으로 생성해줄수 있다.

8. Data Class

  • 코틀린의 data클래스는 아래의 3가지 기능을 모두 포함한다.
    • 인스턴스 간 비교를 위한 equals
    • Hashmap과 같이 해시 기반 컨테이너에서 키로 사용할 수 있는 hashCode
    • 클래스의 각 필드를 순서대로 나열해 줄 수 있는 toString

9. BY

<bash />
interface IWindow { fun getWidth() : Int fun getHeight() : Int } open class TransparentWindow : IWindow { override fun getWidth(): Int { return 100 } override fun getHeight() : Int{ return 150 } } class UI(window: IWindow) : IWindow by window{} val window: IWindow = TransparentWindow() val ui = UI(window) System.out.println("Width : ${ui.getWidth()}, height: ${ui.getHeight()}") // by 키워드를 통해 사용가능

 

10. object keyword

  • 싱글턴 생성
  • object Payrool { val allEmployees = arrayListOf<Person>() .... }
  • 팩터리 메서드와 정적멤버
    • 아래의 코드는 정적팩터리 메서드(A클래스의 동반객체를 companion 이라는 keyword로 생성가능)
    • 위와 같은 이점은 동반객체는 자신을 둘러싼 클래스의 모든 Private 멤버에 접근할 수 있다. 즉 동반객체는 바깥쪽 클래스의 private 생성자를 호출 할 수 있음
<bash />
class A{ companion object { fun bar(){ print("foo hi") } } }
  • 동반 객체 object를 사용할때도 interface를 구현할 수 있다.
<bash />
interface JsonFactory<T>{ fun fromJson(jsonText:String):T } class Persoon (val name:String) { companion object : JsonFactory<Persoon> { override fun fromJson(jsonText: String): Persoon { TODO("Not yet implemented") } } }
profile

규턴의 개발블로그

@규턴이

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!