IT이야기

인터페이스 대신 람다 전달

cyworld 2021. 10. 24. 21:22
반응형

인터페이스 대신 람다 전달


인터페이스를 만들었습니다.

interface ProgressListener {
    fun transferred(bytesUploaded: Long)
}

그러나 람다가 아닌 익명 클래스로만 사용할 수 있습니다.

dataManager.createAndSubmitSendIt(title, message,
object : ProgressListener {
    override fun transferred(bytesUploaded: Long) {
        System.out.println(bytesUploaded.toString())
    }
})

나는 그것을 람다로 대체 할 가능성이 있어야한다고 생각합니다.

dataManager.createAndSubmitSendIt(title, message, {System.out.println(it.toString())})

하지만 오류가 발생합니다. 유형 불일치; 필수 - ProgressListener, 발견됨 - () -> 단위?

내가 무엇을 잘못하고 있지?


@zsmb13이 말했듯이 SAM 변환은 Java 인터페이스에서만 지원됩니다.

그래도 작동하도록 확장 기능을 만들 수 있습니다.

// Assuming the type of dataManager is DataManager.
fun DataManager.createAndSubmitSendIt(title: String, 
                                      message: String, 
                                      progressListener: (Long) -> Unit) {
    createAndSubmitSendIt(title, message,
        object : ProgressListener {
            override fun transferred(bytesUploaded: Long) {
                progressListener(bytesUploaded)
            }
        })
}

Kotlin은 Java 인터페이스에 대한 SAM 변환만 지원합니다.

... 이 기능은 Java interop에서만 작동합니다. Kotlin에는 적절한 함수 유형이 있으므로 함수를 Kotlin 인터페이스 구현으로 자동 변환할 필요가 없으므로 지원되지 않습니다.

-- 공식 문서

매개변수에 람다를 사용하려면 함수가 인터페이스 대신 함수 매개변수를 사용하도록 합니다. (적어도 현재로서는 Kotlin 인터페이스에 대한 SAM 변환 지원에 대한 논의가 진행 중이며 Kotlin 1.1 라이브 스트림에서 가능한 미래 기능 중 하나였습니다.)


파티에 조금 늦었습니다. 인터페이스를 만드는 대신 다음과 같이 데이터 관리자의 인터페이스 대신 함수를 직접 사용하여 컴파일이 만들도록 합니다.

fun createAndSubmitSendIt(title: String, message: String, transferred: (Long) -> Unit) {
    val answer = TODO("whatever you need to do")
    transferred(answer)
}

그런 다음 원하는 대로 사용하면 됩니다! 제 기억이 맞다면 kotlin/jvm 컴파일러가 하는 일은 인터페이스를 만드는 것과 같습니다.

도움이 되기를 바랍니다!


또 다른 해결책은 typealias를 선언하고 어딘가에 주입하고 호출하는 것입니다. 여기 예:

internal typealias WhateverListener = (String) -> Unit

그런 다음 해당 typealias를 클래스에 주입합니다.

class Gallery constructor(private val whateverListener: WhateverListener) {

    ...

    galleryItemClickListener.invoke("hello")

    ...
}

그래서 우리는 람다가 있습니다:

val gallery = Gallery { appNavigator.openVideoPlayer(it) }

솔루션을 찾는 동안 트릭을 보여준 동료 Joel Pedraza에게 감사를 표합니다. <3.


Kotlin과 Java 모두에서 최상의 액세스 경험을 목표로 하는 경우 이 문제에 대한 단일 궁극적인 솔루션은 없습니다.

Kotlin 개발자가 Kotlin 인터페이스에 대한 SAM 변환이 불필요하다고 생각하지 않았다면 "Kotlin Interface" 방법이 궁극적인 솔루션이 될 것입니다.

https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
또한 이 기능은 Java interop에서만 작동합니다. Kotlin에는 적절한 함수 유형이 있으므로 함수를 Kotlin 인터페이스 구현으로 자동 변환할 필요가 없으므로 지원되지 않습니다.

사용 사례에 가장 적합한 솔루션을 선택하십시오.

코틀린 함수 유형

  • 코틀린 API: 완벽
  • Kotlin 액세스: 완벽
  • 자바 액세스:
    • Function1과 같은 자동 생성 매개변수 유형(Java 8 람다의 경우 큰 문제가 아님)
    • return Unit.INSTANCE;무효 반환 대신 자세한 정보를 제공 합니다.
class KotlinApi {
    fun demo(listener: (response: String) -> Unit) {
       listener("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo { success ->
        println(success)
    }
}


public void javaConsumer() {
    new KotlinApi().demo(s -> {
        System.out.println(s);
        return Unit.INSTANCE;
    });
}

코틀린 인터페이스

  • Kotlin API: 추가 인터페이스 정의.
  • Kotlin 액세스: 너무 장황함
  • 자바 액세스: 완벽
class KotlinApi {
    interface Listener {
        fun onResponse(response: String)
    }

    fun demo(listener: Listener) {
       listener.onResponse("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo(object : KotlinApi.Listener {
        override fun onResponse(response: String) {
            println(response)
        }
    })
}

//If Kotlin had supported SAM conversion for Kotlin interfaces. :(
//fun kotlinConsumer() {
//    KotlinApi().demo {
//        println(it)
//    }
//}

public void javaConsumer() {
    new KotlinApi().demo(s -> {
        System.out.println(s);
    });
}

자바 인터페이스

  • Kotlin API: 혼합 Java 코드.
  • Kotlin 액세스: 약간 장황함
  • 자바 액세스: 완벽
class KotlinApi {
    fun demo(listener: Listener) {
        listener.onResponse("response")
    }
}

public interface Listener {
    void onResponse(String response);
}

//Semi SAM conversion
fun kotlinConsumer() {
    KotlinApi().demo(Listener {
        println(it)
    })
}

public void javaConsumer() {
    new KotlinApi().demo(s -> {
        System.out.println(s);
    });
}

여러 방법

  • Kotlin API: 다중 메소드 구현
  • Kotlin Access: 올바른 방법을 사용하면 완벽합니다. 자동 완성은 장황한 방법도 제안합니다.
  • 자바 액세스: 완벽합니다. 자동 완성은 JvmSynthetic주석으로 인해 함수 유형 방법을 제안하지 않습니다.
class KotlinApi {
    interface Listener {
        fun onResponse(response: String)
    }

    fun demo(listener: Listener) {
        demo {
            listener.onResponse(it)
        }
    }

    @JvmSynthetic //Prevents JVM to use this method
    fun demo(listener: (String) -> Unit) {
        listener("response")
    }
}

fun kotlinConsumer() {
    KotlinApi().demo {
        println(it)
    }
}

public void javaConsumer() {
    new KotlinApi().demo(s -> {
        System.out.println(s);
    });
}

자바 API

  • Kotlin API: Kotlin API는 없으며 모든 API 코드는 Java입니다.
  • Kotlin 액세스: 완벽
  • 자바 액세스: 완벽
public class JavaApi {
    public void demo(Listener listener) {
        listener.onResponse("response");
    }

    public interface Listener {
        void onResponse(String response);
    }

}

//Full SAM conversion
fun kotlinConsumer() {
    JavaApi().demo {
        println(it)
    }
}

public void javaConsumer() {
    new JavaApi().demo(s -> {
        System.out.println(s);
    });
}

ReferenceURL : https://stackoverflow.com/questions/43469241/passing-lambda-instead-of-interface

반응형