본문 바로가기

Dev Book

(21)
CH7(7.5). 프로퍼티 접근자 로직 재활용: 위임 프로퍼티 1. 위임 프로퍼티 class Foo { var p : Type by Delegate() } p 프로퍼티는 접근자 로직을 다른 객체에게 위임한다. Delegate 클래스의 인스턴스를 위임 객체로 사용하며 by 뒤에 있는 식을 계산해서 위임에 쓰일 객체를 얻는다. class Foo { private val delegate = Delegate() // 컴파일러가 생성한 도우미 프로퍼티 var p: Type set(Value:Type) = delegate.setValue(..., Value) get() = delegate.getValue(...) } 다음과 같이 컴파일러는 숨겨진 도우미 프로퍼티를 만들고, 그 프로퍼티를 위임 객체의 인스턴스로 초기화 한다. p 프로퍼티는 바로 그 위임 객체에게 자신의 작업을 ..
CH7(7.4). 구조 분해 선언과 component 선언 - 구조 분해를 사용하면 복합적인 값을 분해해서 여러 다른 변수를 한꺼번에 초기화할 수 있다. 내부에서 구조 분해 선언은 관례를 사용하는데, 각 변수를 초기화하기 위해 다음과 같이 componentN이라는 함수를 호출한다. val (a,b) = p ---> val a = p.component1() val b = p.component2() data 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 자동으로 componentN 함수가 만들어진다. 구조 분해 선언은 함수에서 여러 값을 반환하게 할 때 유용하다. data class NameComponents(val name:String, val extension: String) fun splitFilename(fullName:String): NameCompon..
CH7(7.3). 컬렉션과 범위에 대한 관례 1. 인덱스로 원소에 접근: get과 set - 인덱스 연산자도 관례를 따른다. 인덱스 연산자를 사용해 원소를 읽는 연산은 get 연산자 메소드로 변환되고, 원소를 쓰는 연산은 set 연산자 메소드로 변환된다. operator fun Point.get(index: Int): Int{ return when(index){ 0 -> x 1 -> y //주어진 인덱스에 해당하는 좌표를 찾는다. else -> throw IndexOutOfBoundsException("Invalid coordinate $index") } } >>>val p = Point(10, 20) >>>println(p[1]) 20 data class MutablePoint(var x:Int, var y:Int) operator fun Mut..
CH7(7.2). 비교 연산자 오버로딩 1. 동등성 연산자: equals - 코틀린은 관례에 따라 == 연산자 호출을 equals 메소드 호출로 컴파일한다. != 연산자를 사용하는 식도 equals 호출로 컴파일된다. 이 연산자의 경우 내부에서 인자가 널인지 검사해서 널이 아닌 경우에만 equals를 호출한다. a == b -> a?.equals(b) ?: (b==null) class Point2(val x:Int, val y:Int){ override fun equals(obj: Any?): Boolean{ if(obj === this) return true //최적화: 파라미터가 this와 같은 객체인지 if(obj !is Point) return false return obj.x==x && obj.y==y //Point로 스마트 캐스트 ..
CH7(7.1). 산술 연산자 오버로딩 어떤 언어 기능과 미리 정해진 이름의 함수를 연결해주는 기법을 코틀린에서는 관례라고 부른다. 언어 기능을 타입에 의존하는 자바와 달리 코틀린은 관례에 의존한다. 1. 이항 산술 연산 오버로딩 data class Point(val x:Int, val y:Int){ operator fun plus(other:Point):Point{ return Point(x+other.x, y+other.y) } } >>>val p1 = Point(10, 20) >>>val p2 = Point(30, 40) >>>println(p1 + p2) Point(x=40, y=60) 연산자를 오버로딩하는 함수 앞에는 꼭 operator가 있어야 한다. operator 키워드를 붙임으로써 어떤 함수가 관례를 따르는 함수임을 명확히 할..
CH6(6.3). 컬렉션과 배열 1. 널 가능성과 컬렉션 - 컬렉션 안에 널 값을 넣을 수 있는지 여부는 어떤 변수의 값이 널이 될 수 있는지 여부와 마찬가지로 중요하다. 변수 타입 뒤에 ?를 붙이면 그 변수에 널을 저장할 수 있다는 뜻이다. 타입 인자에도 같은 표시를 사용할 수 있다. fun readNumbers(reader: BufferedReader): List{ val result = ArrayList() //널이 될 수 있는 Int 값으로 이뤄진 리스트 for(line in reader.lineSequence()){ try{ val number = line.toInt() result.add(number) } catch(e:NumberFormatException){ result.add(null) //현재 줄을 파싱할 수 없으므로..
CH6(6.2). 코틀린의 원시 타입 1. 원시 타입: Int, Boolean 등 - 원시 타입의 변수에는 그 값이 직접 들어가지만, 참조 타입의 변수에는 메모리상의 객체의 위치가 들어간다. 자바는 참조 타입이 필요한 경우 특별한 래퍼 타입으로 원시타입 값을 감싸서 사용한다. 원시 타입의 값에 대해 메소드를 호출하거나 컬렉션에 담을 수 없기때문에 Collection 등으로 래퍼 타입으로 감싸서 사용한다. 코틀린은 원시 타입과 래퍼 타입을 구분하지 않으므로 항상 같은 타입을 사용한다. - 코틀린에서는 숫자 타입 등 원시 타입의 값에 대해 메소드를 호출할 수 있다. fun showProgress(progress: Int){ val percent = progress.coerceIn(0, 100) println("We are $percent% do..
CH6(6.1-2). 널 가능성 1. 나중에 초기화할 프로퍼티 - 객체 인스턴스를 일단 생성한 다음에 나중에 초기화하는 프레임워크가 많다. 하지만 코틀린에서는 클래스 안의 널이 될 수 없는 프로퍼티를 생성자 안에서 초기화하지 않고 특별한 메소드 안에서 초기화할 수는 없다. 일반적으로는 생성자에서 모든 프로퍼티를 초기화하며, 만약 프로퍼티 타입이 널이 될 수 없는 타입이라면 반드시 널이 아닌 값으로 초기화해야 한다. 그럴 수 없다면 널이 될 수 있는 타입을 제공해야 하거, 이 경우 모든 프로퍼티 접근에 널 검사를 넣거나 !!를 넣어야하므로 보기 나쁜 코드가 된다. - lateinit 변경자를 붙이면 프로퍼티를 나중에 초기화할 수 있다. class MyService{ fun performAction():String = "action" } ..