티스토리 뷰

728x90

 

 

const val과 val의 차이점은?
  • val
private val result = sum(3, 5)
fun sum(a: Int, b: Int) = a + b

val 키워드를 사용했기 때문에 불변 프로퍼티가 되지만, 런타임 시 프로퍼티의 값이 결정된다.
즉, val 프로퍼티에 대입되는 값이 함수의 값이라면, 함수의 인자로 어떤 값을 넣느냐에 따라 달라질 수 있다.

  •  const val
const val result = 8

컴파일 시 결정되는 상수이기 때문에 런타임 시에는 변하지 않는다.
primitive type과 String만 const 키워드를 사용할 수 있다.

 

 

 

코틀린의 primitive type과 wrapper type

자바에는 int, double 등의 primitive type과 Integer, Double 등과 같은 wrapper type이 분리되어있다.
하지만 코틀린에는 이 타입을 구분하지 않고 문맥에 따라 자동으로 primitive type과 wrapper type을 사용한다.
예를 들어 Int?와 같은 nullable한 type이거나 Generic, Collection을 사용하는 경우 wrapper type으로 분류한다.
(JVM에서 primitive type은 null, Generic, Collection을 허용하지 않기 때문)
이러한 특수한 경우가 아니라면 대부분 primitive type을 사용한다.

  • primitive type - 스택 영역에 값을 바로 저장한다.
  • wrapper type - 힙 영역에 값을 저장하며, 스택 영역에는 힙 영역의 메모리 주소를 저장한다.

 

 

초기화 블록(init)이 여러개 있을 때, 실행되는 순서
class Foo {
    init {
        println("first init block")
    }
    init {
        println("second init block")
    }
    init {
        println("third init block")
    }
}

fun main() {
	Foo()
}
출력 결과
first init block
second init block
third init block

가장 상단에 있는 init 블록부터 실행된다.
 
 

주생성자는 없고 부생성자만 있는 경우
  • 실행되는 순서: 프로퍼티 초기화 -> init 블록 -> 부생성자
  • init 블록 호출 시기: 인스턴스 생성시
class Person {
    val fullName: String
    constructor(firstName: String, familyName: String):
        this("$firstName $familyName")
    constructor(fullName: String) {
        this.fullName = fullName
    }
}

fun main() {
    println(Person("k", "hm").fullName)
    println(Person("khm").fullName)
}

위임 호출을 통해 다른 부생성자를 호출할 수 있다.
첫 번째 부생성자는 this를 통해 두 번째 부생성자를 호출한 것이다.
이때 두 번째 부생성자의 인자로 "$firstName $familyName"이 전달되고,
두 번째 부생성자는 그 값으로 fullName 프로퍼티를 초기화한다.


주생성자와 부생성자가 함께 있는 경우와 마찬가지로, 부생성자의 위임을 통해 프로퍼티를 모두 초기화해야 한다.
주생성자가 있는 경우의 부생성자는 위임을 통해 무조건 주생성자를 호출해야 한다.
 

 

함수나 변수의 이름을 잘 지어야 하는 이유, 코딩 컨벤션의 필요성

다른 개발자들 간의 규칙을 설정함으로써 코드의 가독성을 높이고, 효율적으로 유지보수 할 수 있다.

 

 

응집도(Cohesion)

모듈 내부의 구성요소 간의 연관 정도를 나타내는 정도.

응집도가 높은 것: 하나의 모듈이 하나의 기능을 수행하는 것. (모듈의 독립성)

 (<-> 결합도(Coupling): 모듈 간의 상호의존성을 나타내는 정도)
즉 응집도는 높을 수록, 결합도는 높을 수록 좋다.
 

 

캡슐화
  • 클래스 내에 연관된 속성과 행위를 하나로 묶어서, 외부에 감추는 것.
  • 예를 들어 Car 클래스에는 position이라는 속성이 있고, forwardNumber가 4 이상인 경우 position이 증가하는 행위를 가지고 있다. 외부에서는 Car 객체를 전진시키기 위해서 그 행위의 구체적인 구현내용을 알 필요없이 전진하는 행위 자체만 호출하면 된다. (position이 어떤 경우 증가하는지, 얼마만큼씩 증가하는지를 알 필요가 없음)
  • 객체 상태를 객체 자체가 처리하도록 한다. 외부에서는 객체에 메시지만을 던지고(함수 호출, 인자 전달 등) 객체에서는 메시지를 받아서 자신이 가지는 상태를 적절하게 처리한다.
 

 

SOLID (객체지향 설계 5대 원칙)
  • SRP(Single Responsibility Principle): 단일 책임 원칙
    • 하나의 객체는 하나의 책임만을 갖는다.
    • 모듈이 변경되는 이유는 단 하나여야 한다.
  • OCP(Open Closed Priciple): 개방 폐쇄 원칙
    • 확장에는 열려있고, 변경에는 닫혀있어야 한다.
    • 요구 사항이 변해도 기존 구성 요소는 변하지 않고, 기존 구성 요소를 확장하여 요구 사항을 만족시킨다.
    • 구현(클래스)에 의존하기 보다 인터페이스에 의존한다.
  • LSP(Listov Substitution Priciple): 리스코프 치환 원칙
    • 하위 타입은 상위 타입으로 교체할 수 있어야 한다.
  • ISP(Interface Segregation Principle): 인터페이스 분리 원칙
    • 하위 타입의 목적에 맞는 인터페이스만을 제공한다.
    • 일반적인 인터페이스 하나를 제공하기 보다, 인터페이스를 하위 타입의 용도에 맞게 분리하여 구체적인 인터페이스 여러개를 제공한다.
  • DIP(Dependency Inversion Principle): 의존 역전 원칙
    • 어떤클래스를 의존해야 할 때, 클래스를 직접 참조하기 보다는 추상 클래스나 인터페이스를 참조한다.

 

 

메소드와 함수의 차이
  • 함수: 독립된 기능을 수행하는 단위
  • 메소드: 함수의 하위 개념. 클래스와 연관된 함수를 뜻하며, 클래스의 인스턴스를 생성해야 호출할 수 있다.

 

 

상수 판단 기준
코드를 봤을 때 값의 의미가 정확하게 전달되지 않는 경우 상수로 분리한다.
상수의 이름을 통해 값의 의미를 명확히 한다.
 

 

동반 객체
class User private constructor(private val name: String, private val age: Int) {
    companion object {
     	const val AGE_MAX_LENGTH = 5
        
        fun create(name: String, age: Int): User {
            // 여기서 User에 대한 유효성 검사를 수행할 수 있음
            return User(name, age)
        }
    }
}

fun main() {
    println(User.Companion.AGE_MAX_LENGTH)
    println(User.AGE_MAX_LENGTH)
    val user = User.create("hm", 20)
}
  • companion object 키워드를 통해 클래스의 일부를 싱글톤 객체로 선언할 수 있다.
  • 외부에서 클래스의 객체를 생성하지 않아도 클래스명을 통해 동반 객체의 필드나 함수에 접근할 수 있다.
  • 일반 object는 외부에서 object에 접근할 때까지 초기화가 지연되지만, companion object는 클래스가 메모리에 적재될 때 같이 생성된다.
  • 위 코드처럼 외부에서 생성자를 호출할 수 없도록 막아두고, companion object에서 생성자를 호출하여 생성한 인스턴스를 반환하도록 구현할 수 있다. (팩토리 패턴)
  • Outer class에서는 companion object에 접근할 수 있지만, companion object는 Outer class에 접근할 수 없다. (companion object는 인스턴스를 생성하지 않아도 접근할 수 있는데, Outer class의 값은 인스턴스마다 달라지기 때문에)
  • companion object 자체는 static이지만(자바로 디컴파일 했을 때), companion object 내에 있는 일반 필드, 함수는 static이 아니다. const 키워드가 붙은 필드는static이다. 또는 필드나 함수에 @JvmStatic 어노테이션을 붙이면 static으로 생성된다. (추후 공부 후 다시 정리)

 

 

표준 예외
  • 코틀린에 이미 정의되어 있는 예외.
  • 가능한 사용자 정의 예외를 사용하기 보다 표준 예외를 사용하는 것이 좋다.
  • 많은 개발자들이 알고 있기 때문에 더 쉽게 코드를 이해할 수 있다.
  • 코틀린에서 require로 null 체크를 하면 컴파일러가 자동으로 non-nullable 타입으로 형변환 해준다.
  • 자바는 UnckeckedException과 CkeckedException으로 나뉘지만, 코틀린에서는 UncheckedException을 개발자가 따로 처리해주지 않아도 된다.

 

자주 사용하는 표준 예외
  • IllegalArgumentException: 적절하지 못한 값이 함수의 인자로 전달될 때 발생. 코틀린의 require()를 통해 발생시킬 수 있다.
  • IllegalStateException: 코틀린의 check()를 통해 발생시킬 수 있다.
  • IndexOutOfBoundsException: 인덱스 파라미터의 값이 범위를 벗어날 때 발생. 일반적으로 컬렉션이나 배열, String에서 발생한다.
  • ConcurrentModificationException
  • UnsupportedOperationException
  • NoSuchElementException:
 
 
객체, 클래스, 인스턴스의 차이점
  • 클래스: 객체를 생성하기 위한 틀
  • 객체: 모든 인스턴스를 포괄하는 의미. 클래스로부터 생성된 실체
  • 인스턴스: 실제 구현된 구체적인 실체. 실제 메모리에 할당된 객체

인스턴스는 객체의 하위 개념이다.

모든 인스턴스는 객체지만, 모든 객체가 인스턴스일 필요는 없다.

 

 

추후 공부 후 다시 정리

 

 

프로퍼티와 필드의 차이점
  • 프로퍼티
    • 필드와 접근자를 모두 포함하는 개념이다.
    • 실제 값이 저장되어 있는 필드(field) + 데이터를 읽을 때 호출되는 getter + 데이터를 수정할 때 호출되는 setter
class Foo {
    var number: Int = 0
}

fun main() {
    val foo = Foo()
    println(foo.number) // default getter 호출
    foo.number = 5 // default setter 호출
}

위 코드에서 number은 프로퍼티가 아닌 일반 필드처럼 보인다.

하지만 코틀린에서는 프로퍼티에 기본적으로 getter와 setter를 자동으로 생성해준다.

즉, 프로퍼티를 읽거나 쓸 때마다 getter, setter를 호출한다. getter와 setter에서 backing field를 참조하는 형식이다.

class Foo {
    var number: Int = 0
    	get() = field
        set(value) { field = value }
}

실제로는 위와 같은 getter와 setter가 구현되어 있을 것이다.

 

  • 필드
    • 자바에서 클래스의 속성과 동일한 개념이다.
    • getter와 setter를 포함하지 않고 실제 값만을 저장한다.

 

 

디폴트 파라미터 vs 디폴트 아규먼트

디폴트 파라미터: 선언과 관련된 파라미터

디폴트 아규먼트: 실제 디폴트 아규먼트에 대입된 실제 값

 

 

파라미터와 아규먼트
fun sum(a: Int, b: Int) = a + b

fun main() {
    println(sum(3, 5))
}
  • 파라미터(parameter): 함수의 정의에 나열되어 있는 변수들
  • 인자(argument): 함수에 전달되는 실제 값

위 코드에서 a, b는 파라미터이며 3, 5는 인자이다.

 

 

safe call operator ( ?. )

함수나 프로퍼티를 호출하는 값이 null이 아니면 해당 함수와 프로퍼티의 결과를 반환하고, null이면 함수나 프로퍼티를 호출하지 않고 바로 null을 반환하는 연산자

 

 

elvis operator ( ?: )
list?.size ?: 0

 

operator의 왼쪽 값이 null 이라면 operator 오른쪽 값을 반환하고, null이 아니라면 왼쪽 값을 바로 반환한다.
위 식에서는 list가 null이라면 0, list가 null이 아니면 list의 size가 된다.

 

 

not-null assertion operator ( !! )

nullable 타입의 값을 강제로 non-nullable하게 변경한다.

만약 값이 null이라면 바로 NullPointerException을 던진다.

 

 

동등성(Equality)과 동일성(Identity)

동등성(Equality) - 객체의 속성 값이 같은 경우
동일성(Identity) - 객체의 메모리 주소가 같은 경우

 

 

copy를 통해서 새로운 인스턴스를 만들면 불변을 유지할 수 있는 이유

코틀린에서 data class를 선언했을 때 주생성자 프로퍼티를 기준으로 copy() 함수를 자동으로 생성해준다.

data class User(private val name: String, private val age: Int) {
    fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
}

코틀린 공식 문서(https://kotlinlang.org/docs/data-classes.html#copying)에 이러한 코드가 나와있다.
copy() 함수의 파라미터로 User의 주생성자 프로퍼티인 name, age가 존재한다.
반환값이 User(name, age)이므로, 인자로 전달된 name과 age로 새로운 User 인스턴스를 생성한다.


이 name, age 파라미터의 기본값으로 현재 인스턴스의 name, age 프로퍼티 값이 들어가있기 때문에

copy를 호출할 때 프로퍼티 값이 동일하지만 인스턴스의 메모리 값은 다른 인스턴스가 생성되는 것이다.

 

fun main() {
    val user = User("hm", 20)
    val copyUser1 = user.copy()
    val copyUser2 = user.copy(age = 25)
}

copy()를 호출할 때 이름 붙은 인자(named argument)로 원하는 프로퍼티만 변경하여 또 다른 인스턴스를 생성할 수 있다.

 

 

데이터 클래스

주생성자 프로퍼티를 기준으로 equals(), hashcode(), toString(), copy, componentN()을 자동으로 생성해주는 특수한 클래스다.

 

* 데이터 클래스를 선언할 때 주의할 점

// Compile error
// Data class primary constructor must only have property (val / var) parameters
data class User(name: String, age: Int) 

// No error
data class User(private val name: String, private val age: Int)

데이터 클래스의 주생성자에는 생성자 파라미터를 모두 프로퍼티로 선언해야 한다.

 

*

“데이터를 저장하는 클래스”, “DTO”라는 의미로 데이터 클래스로 선언하는 경우가 있는데,

데이터 클래스는 데이터를 담는 클래스라기 보다는 코틀린이 제공하는 기능 중 하나라고 생각하면 쉽다.
데이터 클래스로 선언을 하면 자동으로 생성되는 함수가 많기 때문에 필요한 때에만 신중하게 사용하는 것이 중요하다.

 

 

 

 

 

참고

https://velog.io/@woga1999/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%BD%94%ED%8B%80%EB%A6%B0-Item-6-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%A0%95%EC%9D%98-%EC%98%A4%EB%A5%98%EB%B3%B4%EB%8B%A4%EB%8A%94-%ED%91%9C%EC%A4%80-%EC%98%A4%EB%A5%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC

https://www.nextree.co.kr/p6960/

728x90
250x250
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함