| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- Class
- 우송대학교
- kotlin
- 개발 보드
- deque
- 코틀린
- Android
- List
- Queue
- 리컴포지션
- 스택
- 클래스
- 자료구조
- Stack
- 아두이노
- 라즈베리파이 피코
- State Hoisting
- 큐
- kotlinClass
- 상태 호이스팅
- 재정의함수
- Compoae
- 컴포즈
- Kotlin LinkedList
- Kotlin자료구조
- 안드로이드
- PICO4
- 추상화함수
- 우송대
- LinkedList
- Today
- Total
개발자의 생활
class(클래스) 본문
저는 코틀린 은 자바와 100% 호환되기 때문에 언어적으로 큰 차이가 없다고 생각했습니다.
하지만 코틀린의 철학 때문인지 생각 이상으로 다양한 부분에서 차이점들이 많이 보였는데 그중 클래스 부분에서 많이 다르다는 느낌을 받았습니다.
사실은 코틀린을 간단하게 공부하고 앱을 개발하면서 클래스를 사용할 때 문법적인 부분만 다르다고 생각하고 그대로 사용만 했었습니다. 하지만 코틀린을 더 자세하게 배워야겠다고 생각하였고 이번에는 클래스에 대해 공부한 내용을 정리해 보겠습니다.
Property
<Kotlin>
class KotlinPerson(
var name: String //프로퍼티
)
<Java>
class JavaPerson{
private String name; //필드
public JavaPerson(String name){ //생성자
this.name = name;
}
public String getName() { //getter
return name;
}
public void setName(String name) { //setter
this.name = name;
}
}
위 코드는 완전히 동일한 코드 입니다.
Java에서는 클래스 내부에서 field를 선언하고 getter, setter을 직접 구현해 주어야 했습니다. 그리고 자바의 개념 중 (field + getter + setter) 를 통칭하는 property 가 있었습니다.
Kotlin 에도 이 모든 것들이 동일하게 존재합니다. 하지만 단 한 줄 "var name: String" 이것만으로 property + 생성자가 만들어지게 됩니다.
실제로 두 코드를 사용해 보면
<Kotlin>
fun main(){
val person = KotlinPerson(name = "Name")
person.name = "Kotlin";
println(person.name)
}
class KotlinPerson(
var name: String,
)
출력결과
Kotlin<Java>
public class Java {
public static void main(String[] args) {
JavaPerson person = new JavaPerson("Name");
person.setName("Kotlin");
System.out.println(person.getName());
}
}
class JavaPerson{
private String name; //필드
public JavaPerson(String name){ //생성자
this.name = name;
}
public String getName() { //getter
return name;
}
public void setName(String name) { //setter
this.name = name;
}
}
출력결과
Kotlin
동일한 결과가 출력됩니다.
여기서 중요한 것은 필드의 유무입니다. 코틀린에서 var name: String 이렇게 생성한 것은 필드가 아닌 프로퍼티입니다.
코틀린에서 필드는 언어 차원에서 접근하지 못하도록 만들어 놓았습니다. 이를 Backing Field라고 합니다.
Java에서는 필드를 개발자가 직접 private으로 외부에서 접근하지 못하도록 막아놓고 getter, setter을 사용해서 필드 값을 변경하여야 하지만 이 작업을 Kotlin에서는 자동으로 해주게 됩니다.
그리고 접근을 할 때는 <객체>. <프로퍼티 이름> 으로 값을 대입해 주면 자동으로 setter로 인식하고 대입 연산자가 없으면 자동으로 getter로 동작을 하게 됩니다.
이렇게 동작할 수 있는 이유는 Kotlin 코드를 JVM으로 컴파일하게 되면 Java와 동일한 Bytecode로 변환이 되기 때문입니다.
실제로 위 Kotlin 코드를 Bytecode 로 만들고 다시 Decompile 하게 되면 자바 코드로 변경이 되는데 위 코드를 보겠습니다.
public final class KotlinPerson {
@NotNull
private String name;
public KotlinPerson(@NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
}
@NotNull
public final String getName() {
return this.name;
}
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
}
}
몇몇 추가적인 코드가 붙었지만 구조적으로는 Java와 동일하게 변환이 되었습니다.
커스텀 getter, setter
하지만 여기서 한 가지 의문이 들게 됩니다. Java에서는 getter과 setter을 직접 구현하기 때문에
public void setName(String name) { //setter
this.name = name.toUpperCase();
}
이렇게 여러 기능을 구현할 수 있습니다.
하지만 Kotlin에서는 지금까지의 방식으로는 자동으로 getter, setter 이 만들어지기 때문에 바로 구현을 못 하게 됩니다.
그래서 Java처럼 생성자에서 파라미터 로 받은 값을 자동으로 프로퍼티를 생성하지 못 하도록 하고 내부에서 직접 getter, setter 을 커스텀 해줄 수 있습니다.
class KotlinPerson(name: String) {
var name: String = name
get() = field.uppercase()
set(value) { field = value }
}
생성자 에 val 또는 var 이 없으면 프로퍼티를 생성하지 않고 파라미터로만 받게 됩니다.
그리고 내부에서 var, val 키워드를 사용하면 위와 같이 직접 구현이 가능합니다. 물론 사용할 때는 동일하게 프로퍼티로 접근할 수 있습니다.
*위 코드에서 setter 은 기본적으로 생성해 주는 코드와 동일하기 때문에 없어도 됩니다.*
*val 은 읽기 전용이니 setter 은 사용할 수 없습니다.*
그리고 field라는 키워드가 사용이 되었는데요 name.uppercase() 처럼 작성하지 않은 이유는 무한루프 때문입니다.
name 으로 접근을 하게 되면 또다시 name 프로퍼티를 호출하게 되고 내부에서 계속 반복적으로 자기 자신을 호출하게 되는 문제가 발생합니다. 그래서 Backing Field 사용하는 것입니다.
생성자, 보조 생성자
Kotlin에서 생성자는 파라미터를 만들면서 자동으로 생성된다고 했습니다.
하지만 실행 구문을 작성하기 위해서는 별도의 init 키워드를 사용해야 합니다.
fun main(){
val person = KotlinPerson(name = "Kotlin")
}
class KotlinPerson(
var name: String
) {
init {
println("생성자 실행 | nane = $name")
}
}
출력결과
생성자 실행 | nane = Kotlin
객체가 생성이 되면 자동으로 init 내부가 실행이 됩니다.
그러면 Java처럼 보조생성자는 어떻게 만들 수 있을까요?
그건 constructor을 사용하면 됩니다.
fun main(){
val person = KotlinPerson()
}
class KotlinPerson (
var name: String
) {
init {
println("생성자 실행 | nane = $name")
}
constructor(): this("B"){
println("보조 생성자 실행 | nane = $name")
}
}
실행결과
생성자 실행 | nane = B
보조 생성자 실행 | nane = B위와 같이 객체를 생성할 때 인자값의 개수와 타입이 1:1 연결되어 있는 생성자가 호출이 됩니다.
여기서 Java와 다른점은 보조 생성자를 호출할 시 반드시 주 생성자를 먼저 호출해야 합니다. 주 생성자 호출은 Java 와 동일한 this 키워드를 사용해야 하며 호출 방법은 보조 생성자와 생성자 사이에 : 를 넣어주면 됩니다.
한번 또 다른 보조 생성자를 만들어 보겠습니다. 구분을 위해 age 라는 프로퍼티 를 하나 추가 해 주겠습니다.
fun main(){
val person = KotlinPerson()
}
class KotlinPerson (
var name: String,
var age: Int
) {
init {
println("생성자 실행 | nane = $name | age $age")
}
constructor(name: String): this(name, 20){
println("보조 생성자1 실행 | nane = $name | age $age")
}
constructor(): this("Kotlin"){
println("보조 생성자2 실행 | nane = $name | age $age")
}
}
실행결과
생성자 실행 | nane = Kotlin | age 20
보조 생성자1 실행 | nane = Kotlin | age 20
보조 생성자2 실행 | nane = Kotlin | age 20
이번에는 보조 생성자가 this 키워드를 사용해서 또 다른 보조 생성자를 호출하고 최종적으로는 주 생성자 를 호출하는 구조가 되었습니다.
class KotlinPerson (
var name: String,
var age: Int
) {
init {
println("생성자 실행 | nane = $name | age $age")
}
constructor(name: String): this(){
println("보조 생성자1 실행 | nane = $name | age $age")
}
constructor(): this("Kotlin"){
println("보조 생성자2 실행 | nane = $name | age $age")
}
}
만약 이렇게 보조 생성자끼리 서로 호출하려고 한다면 순환이 되어 버리기 때문에 컴파일 과정에서 에러를 발생시키게 됩니다.
결론적으로 보조 생성자를 호출하면 반드시 주 생성자를 호출하게 되어 있습니다.
왜 Java는 보조 생성자만 호출할 수 있는데 Kotlin 은 반드시 주 생성자를 호출해야 하는지 처음에 저도 의문이 있었습니다.
하지만 이는 Kotlin의 철학 때문이라고 생각합니다.
만약 Java에서 주 생성자에서 일부 필드에 값을 초기화하는 코드가 없다면 나중에 해당 필드의 값을 가져올 때 문제가 생길 수 있습니다. Kotlin에서는 이러한 문제를 방지하기 위해 기본적으로 초기화를 시키도록 만든 것이 아닐까 생각합니다.
하지만 Kotlin의 철학 중 또 하나는 간결한 코드인데 이렇게 하면 코드가 길어지게 되고 가독성이 떨어지게 됩니다.
그래서 default parameter라는 것을 제공합니다. 말 그대로 초기값을 지정할 수 있는 것인데요
fun main(){
val person = KotlinPerson(age = 14)
}
class KotlinPerson (
var name: String = "Kotlin",
var age: Int
) {
init {
println("생성자 실행 | nane = $name | age $age")
}
}
실행결과
생성자 실행 | nane = Kotlin | age 14이렇게 파라미터에 기본으로 값을 지정해 주면 객체를 생성할 때 인자값이 없어도 기본값이 담기게 됩니다.
물론 인자값을 지정해 줄 수도 있습니다.
이상으로 Kotlin의 Class에 대해 조금 더 자세히 공부해 보았습니다. 감사합니다.
'kotlin' 카테고리의 다른 글
| object 키워드(정적 변수, 정적 함수) (1) | 2025.08.31 |
|---|---|
| 상속 (1) | 2025.08.22 |
| 유용한 String 함수 (6) | 2024.10.30 |
| 제네릭 (0) | 2024.10.30 |
| 고차함수, 람다함수 (0) | 2024.10.24 |