1) Data Class
데이터를 담기 위한 클래스
- toString(), hashCode(), equals(), copy() 메서드를 자동으로 생성
- override 하면, 직접 구현한 코드를 사용
- 1개 이상의 property가 있어야 함
- 데이터 클래스는 abstract, open, sealed, inner 를 붙일 수 없음
- 상속이 불가능
1-1) 코틀린/자바 코드 비교
fun main() {
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
class Cat() // 클래스는 property가 없어도 괜찮음
코틀린 코드를 자바 코드로 바꿔보기 Tools > Kotlin > Show Kotlin Bytecode > Decompile
코드의 일부를 살펴보면,
Person 은 그냥 클래스로 구현되었으므로 Get 메서드, 생성자가 있다.
// Person.java
public final class Person {
@NotNull
private final String name;
private final int age;
public static final int $stable;
public Person(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
}
Dog 데이터 클래스는,
copy(), toString(), hashCode(), equals() 4개의 함수가 자동생성된 것을 확인할 수 있음
- copy() 값 복사할 때
- toString() Dog 클래스 내부 값 확인을 할 때, 그냥 Class에서 toString() 하면 해당 객체의 주소값을 가져옴
- toString() : 클래스 내부 데이터를 볼 수 있도록 구현됨
- hashCode()
- equals() 동등성 비교할 때
// Dog.java
public final class Dog {
@NotNull
public final Dog copy(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
return new Dog(name, age);
}
@NotNull
public String toString() {
return "Dog(name=" + this.name + ", age=" + this.age + ')';
}
public int hashCode() {
int result = this.name.hashCode();
result = result * 31 + Integer.hashCode(this.age);
return result;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (!(other instanceof Dog)) {
return false;
} else {
Dog var2 = (Dog)other;
if (!Intrinsics.areEqual(this.name, var2.name)) {
return false;
} else {
return this.age == var2.age;
}
}
}
}
1-2) toString() 출력
fun main() {
val person = Person("주연", 11)
val dog = Dog("삽살개", 2)
println(person.toString())
println(dog.toString())
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
Person 에는 아무 것도 하지 않아서 참조 주소값이 출력됨
Dog 는 데이터 클래스로 내부 데이터를 볼 수 있는 함수가 생성되었으므로, 내부 데이터가 출력됨
1-3) toString 메서드 override 해서 직접 구현
fun main() {
val person = Person("주연", 11)
val dog = Dog("삽살개", 2)
println(person.toString())
println(dog.toString())
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
) {
override fun toString(): String {
return "Dog 직접 구현: $name $age"
}
}
1-4) copy 메서드로 특정 값만 변경
fun main() {
val dog = Dog("삽살개", 2)
println(dog.copy(age = 3)) // age 만 변경된 객체를 얻을 수 있음
}
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
1-5) 데이터 클래스는 상속 불가능
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
data class Corgi (
val cute : Boolean = true
) : Dog()
데이터 클래스가 다른 클래스를 상속받았을 때 4가지 자동 생성된 메서드를 어떻게 생성해야할 지 알 수 없기 때문에
데이터 클래스는 상속이 불가능하다.
2) Sealed Class
추상 클래스로, 상속받은 자식 클래스의 종류를 제한
- 컴파일러가 sealed 클래스의 자식 클래스가 어떤 것인지 앎
- when과 함께 쓰일 때, 장점을 느낄 수 있음
Cat 추상 클래스를 상속받은 클래스는 BlueCat, RedCat, GreenCat 3개 뿐이므로 when 절 안에서 else 에 들어갈 case가 없다.
컴파일러는 Cat 을 상속받은 애들이 누군지 모른다.
fun main() {
val cat : Cat = BlueCat()
val result = when(cat) {
is BlueCat -> "blue"
is RedCat -> "red"
is GreenCat -> "green"
else -> "none"
}
// 상속받은 Cat 의 색깔을 출력
println(result)
}
// sealed class
abstract class Cat
class BlueCat : Cat()
class RedCat : Cat()
class GreenCat : Cat()
여기서 abstract class 를 sealed class 로 변경하면, sealed class Cat
else 구문이 redundant 하다고 알려준다. 컴파일러가 Cat 이 어떤 자식을 가지고 있는지 알게된다. else branch 가 필요없어지는 것
- Recycler View 에서 View 타입 체크하거나
- 에러 체크할 때 유용함
fun main() {
val cat : Cat = BlueCat()
val result = when(cat) {
is BlueCat -> "blue"
is RedCat -> "red"
is GreenCat -> "green"
else -> "none"
}
// 상속받은 Cat 의 색깔을 출력
println(result)
}
// sealed class
sealed class Cat
class BlueCat : Cat()
class RedCat : Cat()
class GreenCat : Cat()
예를 들어, 위 코드에서 class WhiteCat : Cat() 을 추가한다면 (흰 고양이 추가)
컴파일러가 Cat이 어떤 자식을 가지고 있는지 아므로, is WhiteCat → {} 구문이 빠졌다는 것을 알려줌
컴파일러 단계에서 컴파일 에러를 뱉어주기 때문에 실수를 줄일 수 있다.
else 문으로 모호하게 WhiteCat을 처리하는 것이 아니라 명시적으로 처리할 수 있음
3) Class 배경지식
Java 에서도 데이터를 holding 하기 위해 클래스를 많이 만든다.
네트워크 통신을 통해서 서버의 내용을 받는다면,
서버의 response를 그냥 가지고 있는게 아니고, parsing 해서
원하는 데이터를 담을 수 있는 클래스에 넣는 경우가 많음
[목차]
[내용]
1) Data Class
데이터를 담기 위한 클래스
- toString(), hashCode(), equals(), copy() 메서드를 자동으로 생성
- override 하면, 직접 구현한 코드를 사용
- 1개 이상의 property가 있어야 함
- 데이터 클래스는 abstract, open, sealed, inner 를 붙일 수 없음
- 상속이 불가능
1-1) 코틀린/자바 코드 비교
fun main() {
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
class Cat() // 클래스는 property가 없어도 괜찮음
코틀린 코드를 자바 코드로 바꿔보기 Tools > Kotlin > Show Kotlin Bytecode > Decompile
코드의 일부를 살펴보면,
Person 은 그냥 클래스로 구현되었으므로 Get 메서드, 생성자가 있다.
// Person.java
public final class Person {
@NotNull
private final String name;
private final int age;
public static final int $stable;
public Person(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
}
Dog 데이터 클래스는,
copy(), toString(), hashCode(), equals() 4개의 함수가 자동생성된 것을 확인할 수 있음
- copy() 값 복사할 때
- toString() Dog 클래스 내부 값 확인을 할 때, 그냥 Class에서 toString() 하면 해당 객체의 주소값을 가져옴 toString() : 클래스 내부 데이터를 볼 수 있도록 구현됨
- hashCode()
- equals() 동등성 비교할 때
// Dog.java
public final class Dog {
@NotNull
public final Dog copy(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
return new Dog(name, age);
}
@NotNull
public String toString() {
return "Dog(name=" + this.name + ", age=" + this.age + ')';
}
public int hashCode() {
int result = this.name.hashCode();
result = result * 31 + Integer.hashCode(this.age);
return result;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (!(other instanceof Dog)) {
return false;
} else {
Dog var2 = (Dog)other;
if (!Intrinsics.areEqual(this.name, var2.name)) {
return false;
} else {
return this.age == var2.age;
}
}
}
}
1-2) toString() 출력
fun main() {
val person = Person("주연", 11)
val dog = Dog("삽살개", 2)
println(person.toString())
println(dog.toString())
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
Person 에는 아무 것도 하지 않아서 참조 주소값이 출력됨
Dog 는 데이터 클래스로 내부 데이터를 볼 수 있는 함수가 생성되었으므로, 내부 데이터가 출력됨
1-3) toString 메서드 override 해서 직접 구현
fun main() {
val person = Person("주연", 11)
val dog = Dog("삽살개", 2)
println(person.toString())
println(dog.toString())
}
class Person (
val name: String,
val age: Int,
)
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
) {
override fun toString(): String {
return "Dog 직접 구현: $name $age"
}
}
1-4) copy 메서드로 특정 값만 변경
fun main() {
val dog = Dog("삽살개", 2)
println(dog.copy(age = 3)) // age 만 변경된 객체를 얻을 수 있음
}
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
1-5) 데이터 클래스는 상속 불가능
data class Dog (
// 반드시 하나 이상의 property 필요
val name: String,
val age: Int,
)
data class Corgi (
val cute : Boolean = true
) : Dog()
데이터 클래스가 다른 클래스를 상속받았을 때
4가지 자동 생성된 메서드를 어떻게 생성해야할 지 알 수 없기 때문에
데이터 클래스는 상속이 불가능하다.
2) Sealed Class
추상 클래스로, 상속받은 자식 클래스의 종류를 제한
- 컴파일러가 sealed 클래스의 자식 클래스가 어떤 것인지 앎
- when과 함께 쓰일 때, 장점을 느낄 수 있음
Cat 추상 클래스를 상속받은 클래스는 BlueCat, RedCat, GreenCat 3개 뿐이므로 when 절 안에서 else 에 들어갈 case가 없다.
컴파일러는 Cat 을 상속받은 애들이 누군지 모른다.
fun main() {
val cat : Cat = BlueCat()
val result = when(cat) {
is BlueCat -> "blue"
is RedCat -> "red"
is GreenCat -> "green"
else -> "none"
}
// 상속받은 Cat 의 색깔을 출력
println(result)
}
// sealed class
abstract class Cat
class BlueCat : Cat()
class RedCat : Cat()
class GreenCat : Cat()
여기서 abstract class 를 sealed class 로 변경하면, sealed class Cat
else 구문이 redundant 하다고 알려준다. 컴파일러가 Cat 이 어떤 자식을 가지고 있는지 알게된다. else branch 가 필요없어지는 것
- Recycler View 에서 View 타입 체크하거나
- 에러 체크할 때 유용함
fun main() {
val cat : Cat = BlueCat()
val result = when(cat) {
is BlueCat -> "blue"
is RedCat -> "red"
is GreenCat -> "green"
else -> "none"
}
// 상속받은 Cat 의 색깔을 출력
println(result)
}
// sealed class
sealed class Cat
class BlueCat : Cat()
class RedCat : Cat()
class GreenCat : Cat()
예를 들어, 위 코드에서 class WhiteCat : Cat() 을 추가한다면 (흰 고양이 추가)
컴파일러가 Cat이 어떤 자식을 가지고 있는지 아므로,
is WhiteCat → {} 구문이 빠졌다는 것을 알려줌
컴파일러 단계에서 컴파일 에러를 뱉어주기 때문에 실수를 줄일 수 있다.
else 문으로 모호하게 WhiteCat을 처리하는 것이 아니라 명시적으로 처리할 수 있음
3) Class 배경지식
Java 에서도 데이터를 holding 하기 위해 클래스를 많이 만든다.
네트워크 통신을 통해서 서버의 내용을 받는다면,
서버의 response를 그냥 가지고 있는게 아니고, parsing 해서
원하는 데이터를 담을 수 있는 클래스에 넣는 경우가 많음
'안드로이드 앱(Kotlin|Java) > [2025~] 안드로이드 앱' 카테고리의 다른 글
Part0_Ch03_02 Android 기본 요소 (0) | 2025.02.03 |
---|---|
Part0_Ch03_01 Android OS (1) | 2025.02.03 |
Part0_Ch03_02 Android 기본 요소 (0) | 2025.01.24 |
Part0_Ch03_01 Android OS (0) | 2025.01.24 |
Part0_Ch02_06 object, companion object (1) | 2025.01.24 |