Java to Kotlin - Improve Interface to function (listener pattern)

WindSekirun (wind.seo)·2022년 4월 26일
0

이 글은 기존 운영했던 WordPress 블로그인 PyxisPub: Development Life (pyxispub.uzuki.live) 에서 가져온 글 입니다. 모든 글을 가져오지는 않으며, 작성 시점과 현재 시점에는 차이가 많이 존재합니다.

작성 시점: 2017-06-22

리스너 패턴에 사용되던 자바의 인터페이스를 코틀린의 function로 변환하는 과정을 정리해보려 한다.

Step -> { Interface to function }

1. 변환할 Interface 의 구조 알아보기

interface PermissionRequestCallback {
    fun onPermissionResult(resultCode: Int, list: ArrayList<String>)
}

Convert java into kotlin 변환기 사용해서 생성한 인터페이스이다. 하나의 메소드를 가지고 있으며 resultCode: Int, list: ArrayList 의 두개의 파라미터를 가지고 있다.

2. 파라미터 수정

기존에 checkPermission은 fun checkPermission(list: ArrayList<String>, callback: PermissionRequestCallback? = null): Boolean 이런 구조였으나 인터페이스를 Function으로 교체하기 위해 callback: (Int, ArrayList<String>) -> Unit 로 교체해주었다. 두 개의 파라미터를 가지면 순서대로 해당 변수의 타입명을 적어주고, 리턴값은 없어도 되니 Unit를 사용한다. 기본값은 설정하지 않았다. (보통, 권한 요청하고 받으면 바로 작업을 실행하게 만들기 때문이다.)

기본값을 추가시키게 되면 형태는 callback: (Int, ArrayList<String>) -> Unit = {} 가 된다.

만일 파라미터가 하나일 경우 callback: Int.() -> Unit 라고 해도 무방하다.

최종적으로 수정된 checkPermission은 아래와 같다.

 /**
  * check and request Permission which given.
  *
  * @param[array] array of Permission to check
  * @param[callback] callback object
  * @return check result
  */
fun checkPermission(array: Array<String>, callback: (Int, ArrayList<String>) -> Unit): Boolean {
    val permissionList: ArrayList<String> = ArrayList()
    array.forEach { permissionList.add(it) }

    return checkPermission(permissionList, callback)
}

기타 checkPermission, requestPermission 등도 위와 같이 수정했다.

3. callback 파라미터 사용

사용할때는 비교적 간단하다. 기존에는 callback?.onPermissionResult(PERMISSION_ALREADY, list) 이런 식으로 사용했었는데, callback(PERMISSION_ALREADY, list) 로 바꿔주면 된다.

4. 실제로 Kotlin에 사용해보자.

Before

class Callback : RPermission.PermissionRequestCallback {
    override fun onPermissionResult(resultCode: Int, list: ArrayList<String>) {
         alert(message = "Permission result -> $resultCode / Requested Permission: ${TextUtils.join(",", list)}")
    }
}

btnCall.setOnClickListener {
    val arrays : Array<String> = arrayOf(Manifest.permission.CALL_PHONE)
    RPermission.getInstance(this).checkPermission(arrays, Callback())
}

After

btnCall.setOnClickListener {
    val arrays: Array<String> = arrayOf(Manifest.permission.CALL_PHONE)
    RPermission.getInstance(this).checkPermission(array = arrays, callback = { resultCode: Int, list: ArrayList<String> ->
        alert(message = "Permission result -> $resultCode / Requested Permission: ${TextUtils.join(",", list)}")
    })
}

코드도 더 짧아졌고, 무엇보다 class를 선언하지 않아도 된다는게 아주 좋아졌다.

5. 그럼 자바는?

btnCall.setOnClickListener(view -> {
    String[] arrays = new String[]{Manifest.permission.CALL_PHONE};

    RPermission.Companion.getInstance(this).checkPermission(arrays, (integer, strings) -> {
        Utils.alert(PermissionActivity.this,
                    "Permission result ->" + integer + " / Requested Permission: " + TextUtils.join(",", strings));
        return Unit.INSTANCE;
    });
});

람다식을 사용하게 되면 코틀린과 거의 비슷하게 사용할 수 있다.

사용하지 않게 되면..

btnCall.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String[] arrays = new String[]{Manifest.permission.CALL_PHONE};

        RPermission.Companion.getInstance(PermissionActivity.this).checkPermission(arrays, new Function2<Integer, ArrayList<String>, Unit>() {
             @Override
             public Unit invoke(Integer integer, ArrayList<String> strings) {
                    Utils.alert(PermissionActivity.this,
                               "Permission result ->" + integer + " / Requested Permission: " + TextUtils.join(",", strings));
                    return Unit.INSTANCE;
             }
        });
    }
});

결론

람다를 사용하지 않게 되는 경우는 제외하고, 단순히 리스너 패턴으로 사용했던 인터페이스는 function으로 갈아타는게 좀 더 가독성도 있을 뿐더러 코드가 짧아진다는 결론에 도착하였다.

이제 남은건, 다른 클래스들도 이렇게 변환하는 것이다.

profile
Android Developer @kakaobank

0개의 댓글