6월, 2019의 게시물 표시

코틀린 (Kotlin) by 키워드 정리

코틀린 by 키워드 개발하다 보면 하위 클래스가 상위 클래스를 상속해서 상위 클래스 메소드를 오버라이드를 하는 경우가 많습니다. 이러한 상황에서 유지보수를 하다보면 상위 클래스가 변경이되는 경우 하위 클래스가 상위 클래스에 의존하고 있던 상황이 변경되면서 예기치 않은 오류가 발생합니다. 그래서 코틀린에서는 기본적으로 클래스는 final 입니다. 상속가능한 클래스는 open 을 해서 상속이 가능하다는 것을 알려줍니다. 상위 클래스 변경 시 하위 클래스에 영향을 줄 수 있다는 것을 인지시켜 줄수 있습니다. by - 위임 상속을 허용하지 않는 클래스에 새로운 기능을 추가 할때는 위임을 사용할 수 있습니다. 위임을 사용하면 상속하지 않고 기존 기능을 그대로 사용하면서 새로운 기능을 추가 할 수 있습니다. 코틀린에서는 by 키워드 를 사용하여 위임 기능을 사용할 수 있습니다. //상속하지않고 새로운 함수 추가하기 class CountingSet<T> (     val innerSet : MutableCollection <T> = HashSet () ) : MutableCollection <T> by innerSet { //MutableCollection 구현을 innerSet 에 위임      var objectsAdded = 0 //카운트     //override 함수는 HashSet() 대신 구현한 것을 사용      override fun add(element: T): Boolean {           objectsAdded ++    //add할때마다 카운트 증가           return innerSet.add(element)     }     //override 함수는 HashSet() 대신 구현한 것을 사용      override fun addAll(c: Collection<T> ): Boolean {           objectsAdde

코틀린 (Kotlin) 데이터 클래스 정리

데이터 클래스 data class 는 toString , equals , hashcode 함수를 자동 생성 해 줍니다. 그래서 자바와 다르게 코틀린에서 == 은 equals 를 호출합니다. 자바처럼 참조를 호출하려면 === 을 사용합니다. data class Client( val name : String, val code : String) 데이터 클래스 - copy() 함수 제공 데이터 클래스는 copy() 함수를 제공합니다. 코틀린에서 프로퍼티는 val 또는 var 도 가능합니다. 그러나 데이터 클래스의 모든 프로퍼티를 읽기 전용(val)으로 만들어 불변 클래스로 만들기를 권장합니다. 다중 스레드일때 값이 변경되어 동기화할 필요가 줄어들도록 하기 위해서입니다. 객체를 메모리에서 값을 변경하는 것보다 복사본을 만드는 것이 더 좋습니다. public final class Client {     public final Client copy (@NotNull String name, @NotNull String code) {         return new Client(name, code)  //새로운 객체를 생성해서 복사     } } //사용 val client = Client( "zerog" , "1111" ) val clientCopy = client. copy ( "zerog2" , "2222" ) //복사 *개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.

코틀린 (Kotlin) 생성자 정리

주 생성자와 초기화 블록 코틀린의 주 생성자는 클래스 이름 뒤에 괄호로 둘러쌓여 있습니다. open class User constructor (_nicname:String) { // 주생성자     val nickname: String     init {         //주생성자와 함께 초기화 블럭         //init 초기화 블록은 여러개 가능         nickname = _nicname     } } constructor 는 주 생성자 나 부 생성자 를 정의합니다. init 은 초기화 블럭 입니다. 이 초기화 블럭은 주 생성자와 함께 사용되며 init 은 여러개 사용할 수 있습니다. 초기화할 때 로직이 필요한 경우 init 초기화 블록내 로직을 추가하여 초기화 합니다. 조금 더 간단하게 생성자 생성하기 주 생성자 앞에 애노테이션이나 가시성 변경자가 없다면 constructor 는 생략 가능 합니다. 그리고 단순히 주 생성자의 파라미터로 초기화 해줄때는 init 도 생략 가능 합니다. class User ( _nicname:String ) { // 주생성자: constructor 생략 가능     //주 생성자의 파라미터로 초기화, 초기화가 없으면 프로퍼티로도 생성되지 않음,     //getNicname() 함수가 생성 됨     //생성자에서 this.nicname = _nicname 함     val nickname: String = _nicname } 최대한 간략하게 생성자 생성하기 위 코드보다 더 축약이 가능합니다. 주 생성자의 파라미터로 프로퍼티를 초기화하는 경우 생성자 파라미터에 var 또는 val 을 추가하여 간략하게 생성할 수 있습니다 //val 을 사용하면 프로퍼티가 생성 됨 //getNicname() 함수 생성 됨 //생성자에서  this.nicname = nicname 함 class User ( val nicname:String) { }

코틀린 (Kotlin) 내부 클래스 sealed class 정리

내부 클래스 자바에서 내부 클래스는 바깥쪽 클래스에 대해 참조 를 합니다. 그래서 static class 로 선언하여 바깥쪽 클래스에 대한 참조를 제거할 수 있습니다. 코틀린에서는 기본적으로 static 중첩 클래스 와 같습니다. 그리고 바깥쪽 클래스에 대한 참조를 갖고 싶으면 inner 변경자 를 붙입니다. class Button : View {     override fun getCurrentState(): State {         return ButtonState()     }     //내부 클래스     class ButtonState: State {         fun save() {             getCurrentState() //error! 바깥쪽 참조를 할 수 없다         }     }    //inner class 로 바깥쪽 클래스 참조 가능     inner class ButtonStateInner:State {         fun save() {             getCurrentState() //바깥쪽 클래스 참조 가능         }         fun getOuterReference() {             //this@클래스 로 접근 또는 생략가능             this@ Button.getCurrentState() //바깥쪽 클래스 참조 가능         }     } } sealed class 코틀린에서는 상위 클래스를 상속한 하위 클래스 정의를 제한 할 수 있습니다. sealed class Expr { //sealed class 는 open      class Num(val value:Int) : Expr()     class Sum(val left: Expr, val right: Expr) : Expr()     class Total() //when 에 추가를 하지않은 경우 컴파일 에러

코틀린 (Kotlin) open, final, abstract

코틀린 open 접근자 코틀린의 클래스는 디폴트로 final public 입니다. 그래서 상속을 하려면 open class 이여야 하며 open fun 이여야 합니다. open class RichButton : Clickable {     //interface 는 public 이기 때문에 override 가능     override fun click() {           }     //final 이기때문에 override 불가능     fun disable() {           }     //open 이기때문에 override 가능     open fun animate() {           } } RichButton 의 열려있는 animate() 를 상속한 하위 클래스에서 override 하면서 final 로 제한 할 수 있습니다. open class RichRichButton : RichButton() {     //추가로 final 을 붙이면 하위 클래스에서는 override 할 수 없다     final override fun click() {         super.click()     }     final override fun animate() {         super.animate()     }     final override fun showOff() {         super.showOff()     } } 코틀린 추상 클래스 코틀린의 추상클래스는 자바와 같습니다. 다만, 추상메소드가 아닌 메소드는 final 접근자 입니다. 추상 메소드와 open 을 명시적으로 추가한 메소드는  override 할 수 있습니다. abstract class Animated {     //추상 메소드는 열려있다     abstract fun animate()      //추상 클래스이더라도 추상 메소드가 아니면 final 이다     fu

코틀린 (Kotlin) 인터페이스 정리

인터페이스 코틀린의 인터페이스는 자바와 같이 interface 로 시작합니다. interface Clickable {     fun click() } interface 를 구현할 때는 [클래스: 인터페이스] 형식으로 구현됩니다. extends(확장)과 implements(구현) 둘 다 :(콜론) 으로 구현합니다. 코틀린은 1개의 상속과 여러개의 인터페이스를 구현할 수 있습니다. 그리고 override 변경자를 사용해서 자바의 @Override 를 대신합니다. 만약에 override 된 함수 이름과 다른 함수 이름이 중복될경우 둘중 하나는 다른 이름을 사용해야 됩니다. //클래스 상속이나 구현은 : 으로 구분 class Button : Clickable, View { //Clickable은 interface, View 는 상속      override fun click() {           //구현시 override 필수     } } 디폴트 메소드 자바8 에서는 디폴트 메소드인경우 default 를 붙이지만 코틀린에서는 따로 추가되는 변경자가 없이 함수를 구현하면 됩니다. interface Clickable {     fun click()     fun showOff() = Log.d("Clickable showoff") //디폴트 메소드 } 디폴트 메소드는 override 를 하지않은 경우 디폴트로 사용되는 함수 입니다. 같은 함수 이름을 갖는 interface 디폴트 함수 이름이 같은 두개의 인터페이스를 구현해야되는 경우를 생각해봅니다. 디폴트 함수 이름이 같은 이름인 인터페이스를 정의하면 컴파일 에러 가 발생합니다. interface Clickable {     fun click()     fun showOff() = Log.d("Clickable showoff") } interface Focusable {     fu

코틀린 (Kotlin) 로컬 함수

코틀린 로컬 함수 코틀린에서 중복 코드를 간단하게 줄일 수 있는 방법으로 로컬 함수 를 사용할 수 있습니다. 로컬 함수는 함수 내부에 함수를 중첩 해서 만들 수 있습니다. 로컬 함수는 바깥 함수의 파라미터 변수에 접근 할 수 있습니다. 그리고 로컬 함수에 파라미터를 전달 하여 사용할 수도 있습니다. class SaveUser {     /**      * 로컬 함수      */     fun validateUser(user: User) {          //로컬 함수는 바깥 함수의 파라미터와 변수에 접근 할 수 있다          fun validate(value: String, fieldName: String) {             if (value.isEmpty()) {                 throw IllegalArgumentException(                     "Can't save user ${user.id}: " +                             "empty $fieldName" + "value=$value")             }         }          //예외처리로직을 로컬 함수로 공통화 시킬 수 있음         validate(user. name , "Name")         validate(user. address , "Address")     } } 로컬 함수를 사용해서 중복된 코드를 줄일 수 있습니다. 그리고 공통으로 사용하기 위해 작게 쪼게진 함수들이 많은 경우 로컬 함수를 사용해서 공통 처리를 하는데 도움이 될 수 있습니다. 그러나 SaveUser 클래스에 있는  validateUser() 함수를 다른 클래스에서 사용하고 싶은 경우  validateUser() 함수를 그대로 복사 붙여넣기 하거나 User 클

코틀린 (Kotlin) 문자열과 정규식 정리

코틀린 문자열과 정규식 코틀린에서는 자바에는 없는 여러 구분자를 처리할 수 있는 확장함수를 제공합니다. "12.345-6.A".split("\\.|-".toRegex()) //정규식으로 만들어 사용 "12.345-6.A".split(".", "-") //확장 함수로 사용 //결과: [12, 345, 6, A] String 확장 함수 "/document/kotlin/kotlin.txt" 에서 경로와 파일이름, 확장자 를 파싱합니다. 코틀린에서 확장 함수로 제공해주는 함수를 사용하면 정규식 사용없이 손쉽게 문자열을 파싱할 수 있습니다. fun parsePath(path: String ) {         val directory = path. substringBeforeLast ("/")         val fullName = path. substringAfterLast ("/")         val fileName = fullName. substringBeforeLast (".")         val extension = fullName. substringAfterLast (".")         println("Dir: $ directory , name: $ fileName , ext: $ extension ") } 3중 따옴표 문자열 3중 따옴표 문자열에서는 역슬래시(\) 를 포함하여 따로 이스케이프할 필요가 없습니다. 일반적으로 마침표(.) 기호를 이스케이프하려면 \\. 이라고 사용하지만 3중 따옴표 문자열에서는 \. 이라고 사용합니다. fun parsePathRegx(path: String ) {         // """ 으로 묶은 정규표현식은 이스케이프하지 않

코틀린 (Kotlin) 리스트, 가변 인자, 중위 호출, 구조 분해

코틀린 리스트 코틀린의 리스트는 listOf (...) 으로 생성할 수 있습니다 그리고 setOf (...) 로 Collection 을 생성할 수도 있습니다. val strings: List <String> = listOf("1","2","3") strings. last () //last() 는 확장 함수 val numbers: Collection <Int> = setOf(1,10,100) numbers. max () //max() 는 확장 함수 가변 인자 함수 자바에서는 가변 인자로 'String ...' 을 사용합니다. 코틀린에서는  vararg 변경자를 붙입니다. varag 의 배열을 펼치기 위해서는 *(스프레드) 연산자 를 이용할 수 있습니다 //자바 public static void main( String... args ) { ... } //코틀린 fun main( vararg  values: String ) {     val list = listOf ("list=", *values ) //*연산자가 배열 내용을 펼쳐줍니다     println(list) } main("a","b","c") //"[list=, a, b]" 출력 중위 호출 1 to "one" 처럼 to 를 중간에 사용하는 것을 중위 호출 이라고 합니다. 이때 to 는 일반 메소드 입니다. '객체 메소드이름 유일인자' 사이에는 공백 이 들어가야 합니다. val mapData = mapOf (     1 to "one" ,     //1(객체) to(메소드이름), "one"(유일한 인자)     2 to "two" ,    //to 메소드를 공백으로