❓ value class and data class
@JvmInline
value class LottoNumber2(val number: Int){
init {
require(number in 1..45) { "로또 번호는 1이상 45이하여야 합니다." }
}
}
data class LottoNumber(val number: Int) {
init {
require(number in 1..45) { "로또 번호는 1이상 45이하여야 합니다." }
}
}
LottoNumber
, LottoNumber2
클래스는 숫자 하나를 감싸고있는 래퍼 클래스다. 이를 통해 가독성이 좋아지는 효과를 볼 수 있고, 원하는 값이 들어오지 않는 경우 예외를 날려줄 수 있다.
위에 작성한 코드를 디컴파일 해서 비교해보자.
public LottoNumber(int number) {
boolean var10000;
label17: {
super();
this.number = number;
int var2 = this.number;
if (1 <= var2) {
if (45 >= var2) {
var10000 = true;
break label17;
}
}
var10000 = false;
}
boolean var4 = var10000;
if (!var4) {
int var3 = false;
String var5 = "로또 번호는 1이상 45이하여야 합니다.";
throw new IllegalArgumentException(var5.toString());
}
}
...
우리는 생성자만 볼 것이다.
data class의 init{}
안에 유효성 검증을 하는 코드가 있었고, 디컴파일의 결과를 보면 유효성 검증 코드가 생성자에 존재하는 것을 볼 수 있다. 인스턴스를 생성하는 동시에 유효성 검증을 하기 때문에 어떻게 보면 당연하다고 볼 수 있다.
전체코드를 가져오지 않아서 보이지는 않지만 실제로 디컴파일을 진행해보면 data class는 component1()
, copy()
, toString()
, hashCode()
, equals()
가 구현되어 있는 것을 볼 수 있다.
public final class LottoNumber2 {
private final int number;
// $FF: synthetic method
private LottoNumber2(int number) {
this.number = number;
}
public static int constructor_impl/* $FF was: constructor-impl*/(int number) {
boolean var10000;
label17: {
if (1 <= number) {
if (45 >= number) {
var10000 = true;
break label17;
}
}
var10000 = false;
}
boolean var1 = var10000;
if (!var1) {
int var2 = false;
String var3 = "로또 번호는 1이상 45이하여야 합니다.";
throw new IllegalArgumentException(var3.toString());
} else {
return number;
}
}
...
constructor\_impl
함수만 보도록하자.
value class도 init{}
안에 유효성 검증을 하는 코드가 있고, 디컴파일의 결과를 보면 유효성 검증 코드가 constructor\_impl
라는 static 메서드안에 생성되어 있는 것을 볼 수 있다.
유효성검증이 끝나고 이상이 없으면 반환하는 값은 number다. 여기서 number는 생성자에서 파라미터로 넣어준 number다. 엥? class 생성자의 반환 값이 int형태라니. 뭔가 이상하지 않은가?
사실 value class는 코드를 작성할 때는 객체를 생성하고 객체를 넘겨주는 것처럼 돌아다니지만 실제로는 int 하나가 돌아다니는 것이다. 실제로 객체를 생성하는 것이 아니기 때문에 객체를 생성하는 비용을 줄여주는 역할을 한다.
그리고 실제로 디컴파일된 전체 코드를 보면 data class와는 다르게 toString()
, hashCode()
, equals()
까지만 구현되어 있는 것을 볼 수 있다.
💡 value class
value class는 객체를 생성하는 비용을 줄여주는 클래스로 이해할 수 있다. 조금 더 자세히 말하면 JVM이 바이트코드로 컴파일하는 과정에서 객체를 제거하고 value class의 프로퍼티로 대체한다. 위에서 봤던 예시에 붙여보면 value class를 디컴파일한 코드의 생성자에서 LottoNumber
가 아닌(객체를 인스턴스화하지 않음) int 하나를 반환하는 이유를 알 수 있게 된다.
그리고 이 친구를 사용하기 위해서는 value
키워드, @JvmInline
어노테이션을 붙여주면 된다.
요약
value class는 객체를 인스턴스화하지 않는 방법을 사용하여 객체 생성의 비용을 줄여주는 클래스다.
data class와의 차이는 다음과 같다.
- 자동으로 생성하는 메서드가 다르다.
- value class는 자동으로 생성하는 메서드가
toString()
,hashCode()
,equals()
다. - data class는 자동으로 생성하는 메서드가
component1()
,copy()
,toString()
,hashCode()
,equals()
다.
- value class는 자동으로 생성하는 메서드가
- === 연산
- value class는 == 연산은 지원하지만 === 연산자는 사용할 수 없다. 동등성은 비교할 수 있지만 동일성은 비교할 수 없다.
- data class는 동등성과 동일성 모두 비교할 수 있다.
- val 프로퍼티
- value class는 프로퍼티가
Immutable
이어야만 한다. - data class는 프로퍼티가
Mutable
인지,Immutable
인지 관계없다.
- value class는 프로퍼티가
- 하나의 프로퍼티
- value class는 프로퍼티를 하나만 가질 수 있다.
- data class는 프로퍼티를 여러개 가질 수 있다.