[Android] @JvmOverloads란?
https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.jvm/-jvm-overloads/
JvmOverloads
Instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values. If a method has N parameters and M of which have default values, M overloads are generated: the first one takes N-1 parameters (all but the last
kotlinlang.org
이 함수에 대해 기본 매개변수 값을 대체하는 오버로드를 생성하도록 코틀린 컴파일러에 지시한다. 메서드에 n개의 매개변수가 있고 그 중 m개가 기본값이면 첫 번째 오버로드는 기본값을 사용하는 마지막 매개변수를 제외하고 모두 n-1개, 두 번째는 n-2개 등 m개의 매개변수를 사용하는 식으로 생성된다
@JvmOverloads 어노테이션은 코틀린 함수나 생성자에서 기본값이 지정된 매개변수가 있을 때 자바 코드에서 호출할 수 있게 여러 오버로드된 메서드를 자동 생성해주는 어노테이션이다.
코틀린에선 기본값이 있는 매개변수를 쓸 수 있지만 자바는 쓸 수 없기 때문에 모든 인자를 명시적으로 전달해야 하고, 자바 호출자에게 여러 오버로드 메서드를 노출하기 위해 이 어노테이션을 써야 한다.
이것은 커스텀 뷰를 만들기 위해 프레임 레이아웃, 리니어 레이아웃 등을 상속한 클래스를 만들 때 사용할 수 있다. 아래는 예시 코드다. xml은 만들지 않았다.
class MyButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr)
그럼 이건 왜 쓰는 것인가? 위 코드에서 어노테이션을 제거하고 FrameLayout을 상속하게만 수정하면 컴파일 에러가 발생하는데, 에러를 해결하기 위해 팝업을 열면 아래처럼 표시된다.

매개변수가 1개, 2개, 3개, 4개 있는 생성자 중 하나를 선택할 수 있는데 저 중 하나라도 선택하면 컴파일 에러는 사라진다.
그럼 해결된 건가? 1개, 3개, 4개 중 하나를 추가한 다음, 메인 액티비티 xml에 저 MyButton 클래스를 추가하고 앱을 실행하면 아래 에러가 발생한다. 아래는 매개변수가 4개인 생성자를 추가했을 때 발생하는 에러다.
android.view.InflateException: Binary XML file line #23 in com.example.regacyviewpractice:layout/item_my_data: Binary XML file line #23 in com.example.regacyviewpractice:layout/item_my_data: Error inflating class com.example.regacyviewpractice.presentation.MyButton
Caused by: android.view.InflateException: Binary XML file line #23 in com.example.regacyviewpractice:layout/item_my_data: Error inflating class com.example.regacyviewpractice.presentation.MyButton
Caused by: java.lang.NoSuchMethodException: com.example.regacyviewpractice.presentation.MyButton.<init> [class android.content.Context, interface android.util.AttributeSet]
에러 내용을 보면 MyButton을 inflate하는 과정에서 뻑난 것 같은데, MyButton 코틀린 코드를 자바 코드로 변환하면 아래처럼 표시되는 걸 볼 수 있다.
@Metadata(
mv = {1, 9, 0},
k = 1,
xi = 48,
d1 = {"\u0000\u001e\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\b\n\u0002\b\u0003\u0018\u00002\u00020\u0001B'\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\b\u0010\u0004\u001a\u0004\u0018\u00010\u0005\u0012\u0006\u0010\u0006\u001a\u00020\u0007\u0012\u0006\u0010\b\u001a\u00020\u0007¢\u0006\u0002\u0010\t¨\u0006\n"},
d2 = {"Lcom/example/regacyviewpractice/presentation/MyButton;", "Landroid/widget/FrameLayout;", "context", "Landroid/content/Context;", "attrs", "Landroid/util/AttributeSet;", "defStyleAttr", "", "defStyleRes", "(Landroid/content/Context;Landroid/util/AttributeSet;II)V", "app_debug"}
)
public final class MyButton extends FrameLayout {
public MyButton(@NotNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Intrinsics.checkNotNullParameter(context, "context");
super(context, attrs, defStyleAttr, defStyleRes);
}
}
생성자 시그니처가 Context, AttributeSet, int, int만 있는 걸 볼 수 있다.
잠깐 다른 이야기로 넘어가면, LayoutInflater는 xml 파일을 입력으로 받아서 뷰 객체를 빌드하는 역할을 한다. 커스텀 뷰를 메인 액티비티 xml에 선언한 후 앱을 실행하면 LayoutInflater는 뷰 객체를 빌드하기 위해 MyButton 클래스에서 필요한 생성자를 찾는다. 이 때 찾는 생성자가 없으면 NoSuchMethodException을 일으키고 앱이 다운된다.
그래서 LayoutInflater가 MyButton을 인스턴스화하는 과정에서 매개변수 4개짜리 생성자만 정의했으니 필요한 생성자를 찾지 못해 NoSuchMethodException을 일으킨 것이라고 볼 수 있다.
에러를 해결하려면 아래처럼 부 생성자들을 작성해서 해결할 수 있다.
class MyButton: FrameLayout {
constructor(context: Context) : this(context, null, 0)
constructor(context: Context, attr: AttributeSet?) : this(context, attr, 0)
constructor(context: Context, attr: AttributeSet?, defStyleAttr: Int) : super(context, attr, defStyleAttr)
}
찾아보니 위와 같이 생성자 3개를 정의하는 게 관례인 듯하다.
그러나 커스텀 뷰를 만들 때마다 저 생성자들을 전부 정의해야 하는 건가? 좀 더 편하게 같은 역할을 하는 코드를 짤 수는 없는가?
그래서 @JvmOverloads 어노테이션이 사용된다. 생성자 3개를 생성하지 않아도, 처음에 본 코드로 수정하면 문제 없이 커스텀 뷰를 사용할 수 있게 된다.
class MyButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr)
아래는 또 다른 코틀린 공식문서에서 @JvmOverloads를 설명하는 부분이다.
https://kotlinlang.org/docs/java-to-kotlin-interop.html#overloads-generation
Calling Kotlin from Java | Kotlin
kotlinlang.org
일반적으로 기본 매개변수 값을 써서 코틀린 함수를 만들면 모든 매개변수가 포함된 전체 서명으로만 자바에 표시된다. 자바 호출자에게 여러 오버로드를 노출하려면 @JvmOverloads를 쓰면 된다. 이 어노테이션은 생성자, 정적 메서드 등에도 사용할 수 있다. 인터페이스에 정의된 메서드를 포함한 추상 메서드에는 사용할 수 없다
기본값이 있는 모든 매개변수에 대해 이 매개변수와 매개변수 목록의 오른쪽에 있는 모든 매개변수가 제거된 추가 오버로드가 하나씩 생성된다
부 생성자에서 말한 대로 모든 생성자 매개변수에 대한 기본값이 있는 경우 매개변수가 없는 공용 생성자가 생성된다. 이는 @JvmOverloads가 지정되지 않아도 작동한다
그러나 @JvmOverloads가 항상 만능인 건 아니다. defStyleAttr의 값에 확장하려는 뷰의 기본값이 아닌 @JvmOverloads의 생성자에 있는 기본값이 들어갈 수 있다. 이 때는 부 생성자들을 작성하고 그 뒤에 전부 super를 명시하면 된다.
class MyButton: FrameLayout {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet?): super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
}
또한 API 21(롤리팝) 미만의 구형 기기에선 파라미터가 4개인 생성자를 호출할 수 없어서 파라미터가 3개인 생성자만 호출된다. minSdk가 21 미만인 경우는 흔하지 않겠지만 해당하는 경우 아래처럼 할 수 있다.
open class RadioButton : TextView, Checkable {
@JvmOverloads
constructor(context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = android.R.attr.radioButtonStyle,
defStyleRes: Int = R.style.guide_RadioButton)
: super(context, attrs, defStyleAttr, defStyleRes) {
initRadioButton(attrs, defStyleAttr, defStyleRes)
}
fun initRadioButton(attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) {
// ..
}
}
Android: using LayoutInflater.inflate to pass custom parameters to a constructor
This has been bothering me for a while, and none of my searching has yielded results. If I have a custom GUI element, I can use a LayoutInflater to inflate it as I would a normal component. The inf...
stackoverflow.com
https://damon-911.tistory.com/entry/Android-JvmOverloads
[Android] @JvmOverloads
View의 생성자 View의 생성자에는 총 4가지가 있습니다. 종류 설명 View(Context context) 코드 상에서 View 객체를 생성할때 사용하는 생성자 View(Context context, AttributeSet attrs) XML로부터 View를 객체를 생성(i
damon-911.tistory.com
https://medium.com/@Zielony/guide-to-android-custom-views-constructors-df47476e334c
Guide to Android custom views: constructors
I’ve been designing, writing and publishing Android custom views for the past 5 years now. I guess it’s about time to sum it up and share…
medium.com