I'm trying to pass a function as parameter to a class extension method.
When doing
fun Router.handleJsonGet(path: String, method: () -> Any) {
this.get(path).handler {
it.response().putHeader("Content-Type", "application/json").end((gson.toJson(method())))
}
}
I can easily wrap a function call inside a lambda and everything's working fine.
router.handleJsonGet("/business/list", { BusinessService.listBusinesses() })
Now I want to include a map of GET params and I'm getting stuck.
fun Router.handleJsonGet(path: String, method: (any:Any) -> Any) {
this.get(path).handler {
val map: MultiMap = it.request().params() ?: MultiMap.caseInsensitiveMultiMap()
it.response().putHeader("Content-Type", "application/json").end((gson.toJson(method(map))))
}
}
How do I make use of this method extension now? The wrap-with-lambda trick doesn't seem to work when the function requires a parameter.
I was expecting something like:
router.handleJsonGet("/business/list", BusinessService::listBusinesses)
with
object BusinessService {
fun listBusinesses(any: Any) : List<Business> {
// do something with any as MultiMap
return Business.findAllBusinesss();
}
}
to work, but IntelliJ says Type Mismatch
Any idea how I should be passing a function that requires a parameter to a class extension method and how I should be calling it?
The syntax you tried to use (BusinessService::listBusinesses) is not yet supported for objects in Kotlin 1.0.4, it is only planned for Kotlin 1.1 (Bound callable references).
Seems like you just need to use lambda with argument, either explicit or implicit it:
router.handleJsonGet("/business/list") { a -> BusinessService.listBusinesses(a) }
router.handleJsonGet("/business/list") { BusinessService.listBusinesses(it) }
Simplified example that demonstrates it:
object BusinessService {
fun listBusinesses(any: Any): List<String> {
return listOf("a", "b", "c");
}
}
fun handleJsonGet(path: String, method: (Any) -> Any) {
val map: Map<String, Int> = mapOf("a" to 1)
method(map)
}
fun main(args: Array<String>) {
handleJsonGet("1") { BusinessService.listBusinesses(it) }
}
Call it like this:
router.handleJsonGet("/business/list", {
BusinessService.listBusinesses(anyParamHere)
})
Related
I have two class TestTarget, MockTarget and test code below. If the TestTarget has two functions with same name and same count of parameter, the any() is ambiguous. I need to assign the type to any(ClassType). But what is the type of () -> Unit? I have tried Function0 and it doesn't work. Can anyone help?
Class TestTarget:
Class TestTarget(private val mockTarget: MockTarget) {
fun testFunction() {
// some logic to be tested.
// call mockTarget.doSomething.
}
}
Class MockTarget
Class MockTarget {
fun doSomething(callback: () -> Unit) {
// some logic here.
}
fun doSomething(listener: OtherType) {
// Test code works without this function.
}
}
Test Code:
// setup mocks.
#Test
fun `verify testFunction`() {
`when`(mockTarget.doSomething(any())).thenAnswer { invocation ->
// callback here.
}
}
Replace org.mockito.Mockito with org.mockito.kotlin.*
and you can code like this
val anyLambda = any<() -> Unit>()
val anyListener = any<OtherType>()
I am working on an android application using kotlin as my primary language. I needed to ask some runtime permissions for some parts of the app. So instead of writing similar boilerplate code to ask for the permissions I decided to to write a separate static function that checks for the permission, and run a method. Here is the static function
fun permissionExecution(childFragment: Fragment, permission: String, expression: ????) {
Dexter.withActivity(childFragment.requireActivity())
.withPermission(permission)
.withListener(object : PermissionListener {
override fun onPermissionGranted(response: PermissionGrantedResponse?) {
expression()
}
override fun onPermissionRationaleShouldBeShown(
permission: PermissionRequest?,
token: PermissionToken
) {
token.continuePermissionRequest()
}
override fun onPermissionDenied(response: PermissionDeniedResponse) {
if(response.isPermanentlyDenied) {
openSettings(childFragment)
}
}
}).check()
}
This code works perfectly when I passed in methods with no arguments. But I have some situations where I will like to pass in methods with different arguments types.
calling the method like this permissionExecution(childfragment, permission, foo(string))
calling the same method like this permissionExecution(childfrgment, permission, bas(string, Int))
what class type can I use for the espression argument in the permissionExecution() method
It doesn't make sense to include the function parameters in the definition of the lambda arument. You already have everything you need to call these other functions:
permissionExecution(myFragment, Manifest.permission.RECORD_AUDIO) {
foo(myString)
}
permissionExecution(myFragment, Manifest.permission.CAMERA) {
bar(myString, myInt)
}
If you need the PermissionGrantedResponse to determine what these parameters are, you can define that as the function input:
fun permissionExecution(childFragment: Fragment, permission: String, expression: (PermissionGrantedResponse) -> Unit) {
Dexter.withActivity(childFragment.requireActivity())
.withPermission(permission)
.withListener(object : PermissionListener {
override fun onPermissionGranted(response: PermissionGrantedResponse) {
expression(response)
}
//...
}
//...
permissionExecution(myFragment, Manifest.permission.RECORD_AUDIO) { response ->
foo(response.permissionName)
}
If I understand correctly, you have implemented a common static func that checks permission and pass some lambda to it that will be invoked when permission is granted. I do not understand why you these lambdas need some argument parameters. Is the below implementation what you desire?
class CameraFragment {
fun onCreateView() {
permissionExecution(childFragment = arg1, permission = "perm", expression = {
// open camera
})
}
}
class LocationActivity {
fun onCreate() {
permissionExecution(childFragment = arg1, permission = "perm", expression = {
fetchLocation()
})
}
fun fetchLocation() {
// get location, do stuff
}
}
here is a not so elegant solution. I create a wrapper class with a single method interface like this
class Permissions(val childFragment: Fragment, private val permission: String, val runExpression: RunExpression) {
interface RunExpression{
fun expression()
}
fun permissionExecution() {
Dexter.withActivity(childFragment.requireActivity())
.withPermission(permission)
.withListener(object : PermissionListener {
override fun onPermissionGranted(response: PermissionGrantedResponse?) {
runExpression.expression()
}
override fun onPermissionRationaleShouldBeShown(
permission: PermissionRequest?,
token: PermissionToken
) {
token.continuePermissionRequest()
}
override fun onPermissionDenied(response: PermissionDeniedResponse) {
if(response.isPermanentlyDenied) {
openSettings(childFragment)
}
}
}).check()
}
}
then call the class like this each time I need the permission
1.
Permissions(this, Manifest.permission.READ_CONTACTS, object : Permissions.RunExpression {
override fun expression() {
startActivityForResult(intent, PICK_CONTACT)
}
}).permissionExecution()
2.
Permissions(this, Manifest.permission.READ_CONTACTS, object : Permissions.RunExpression {
override fun expression() {
writeFileToLocation(file, locationPath)
}
}).permissionExecution()
a better way i find by adapting the solution here is firstly, create an interface like this
interface RunExpression{
fun expression()}
then use the interface in the function signature
fun permissionExecution(childFragment: Fragment, permission : String, runExpression: RunExpression) {
Dexter.withActivity(childFragment.requireActivity())
.withPermission(permission)
.withListener(object : PermissionListener {
override fun onPermissionGranted(response: PermissionGrantedResponse?) {
runExpression.expression()
}
override fun onPermissionRationaleShouldBeShown(
permission: PermissionRequest?,
token: PermissionToken
) {
token.continuePermissionRequest()
}
override fun onPermissionDenied(response: PermissionDeniedResponse) {
if(response.isPermanentlyDenied) {
openSettings(childFragment)
}
}
}).check()}
then wherever I want to call the function, I save the anonymous object that implement the interface into a variable
val startMyActivity = object : RunExpression {
override fun expression() {
startActivityForResult(intent, PICK_CONTACT)
}
}
then call the function with the variable
permissionExecution(this, Manifest.permission.READ_CONTACTS, startMyActivity)
I am trying to use internationalization in a Kotlin application using the tornadofx framework.
I have created a properties file and depending on the selected language the correct file is loaded. But when I want to change the language in the running application the UI does not update accordingly.
For internationalization you should use a companion object to get the related translation anywhere in your application.
First of all your translation class should know which is the actual selected language/locale. For this I use an enum with the possible locales for the application:
fun setLocale(locale: SupportedLocale) {
if (SupportedLocale.supportedLocals.contains(locale)) {
Locale.setDefault(locale.local)
actualLocal = locale.local
//Good practice would be to store it in a properties file to have the information after restart
} else {
//Throw a warning or sth with your preferred logger
}
}
Then we need a method which gets the particular string value from your resource bundle like:
operator fun get(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): String {
val bundle = ResourceBundle.getBundle(BUNDLE_NAME, actualLocal)
return MessageFormat.format(bundle.getString(key), *args)
}
In JavaFx applications (also TornadoFX) you should use StringBindings (https://docs.oracle.com/javase/8/javafx/api/javafx/beans/binding/StringBinding.html) for example to bind a label text property to your translated string. For that we will implement a special method:
fun createStringBinding(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): StringBinding {
return Bindings.createStringBinding(Callable { get(key, *args) }, Settings.languageProperty())
}
Now you can use your object like this:
textProperty().bind(MyLang.createStringBinding("MyApp.MyTranslation"))
Here an runnable example:
MyLang.kt
enum class SupportedLocale(val local:Locale) {
ENGLISH(Locale.ENGLISH),
GERMAN(Locale.GERMAN);
companion object {
val supportedLocals: List<SupportedLocale>
get() = SupportedLocale.values().toList()
}
}
class MyLang {
companion object {
private const val BUNDLE_NAME = "Language" //prefix of your resource bundle
private var actualLocal = Locale.getDefault()
fun setLocale(locale: SupportedLocale) {
if (SupportedLocale.supportedLocals.contains(locale)) {
Locale.setDefault(locale.local)
actualLocal = locale.local
//Good practice would be to store it in a properties file to have the information after restart
} else {
//Throw a warning or sth with your preferred logger
}
}
operator fun get(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): String {
val bundle = ResourceBundle.getBundle(BUNDLE_NAME, actualLocal)
return MessageFormat.format(bundle.getString(key), *args)
}
fun createStringBinding(#PropertyKey(resourceBundle = BUNDLE_NAME) key: String, vararg args: Any): StringBinding {
return Bindings.createStringBinding(Callable { get(key, *args) }, Settings.languageProperty())
}
}
}
fun main() {
println("My translation: " + MyLang.createStringBinding("MyApp.MyTranslation").get())
//The get() here is only to get the string for assign a property its not needed like in the example
}
If you need any explanations or its unclear. Just ask! Its just written down maybe I forgot something to explain.
I have an interface
private interface WithTokenExecutor<T> {
fun execute(token: Token): Single<T>
}
and a function withToken that asynchronously gets an access token then returns the executed WithTokenExecutor parameter with the new token.
private fun <T> withToken(executor: WithTokenExecutor<T>): Single<T> {
return essentialApiTokenProvider.getTokenObservable(true) // returns an observable with the token
.flatMap { token -> executor.execute(token)) }
}
Then I to call the function with the:
fun getAppData(apps: List<String>): Single<AppsList> {
return withToken(object : WithTokenExecutor<AppsList> {
override fun execute(token: Token): Single<AppsList> {
return api.getDetails(token) // retuns a Single<AppsList>
}
})
}
This works, so my question is is it possible to change the return statement from an anonymous class to lambda even if the return type of the withToken and the WithTokenExecutor functions are generic?
I have tried doing this:
return withToken({ token -> api.getDetails(token) })
but the compiler says:
Type inference failed: fun <T> withToken(executor: StoreManager.WithTokenExecutor<T>):Single<T> cannot be applied to ((???) -> Single<AppsList>)
Is there a way to explicitly define the return type of these functions while still keeping the lambda?
If you have the option of modifying your declaration of WithTokenExecutor to:
typealias WithTokenExecutor<T> = (t : Token) -> Single<T>
...you will be able to implement your getAppData like this:
fun getAppData(apps: List<String>): Single<AppsList> = withToken { api.getDetails(it) }
If changing the declaration is not possible, it seems like you are out of luck until Kotlin 1.4 as #Pawel points mentions in the comments.
With all the well-known single-function listeners we can use a simpler lambda notation
view.setOnClickListener { do() }
instead of the original, longer Java way of
view.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
do()
}
})
But what exactly makes this work? I tried to do the same with my own listener:
private var listener: OnCopyPasteClickListener? = null
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: OnCopyPasteClickListener) {
listener = onCopyPasteClickListener
}
and while the long approach works just fine:
copypaste.setOnCopyPasteClickListener(object : CopyPasteMenu.OnCopyPasteClickListener {
override fun onPasteClick(text: String) {
do(text)
}
})
I can't make it accept the short one:
copypaste.setOnCopyPasteClickListener {
do(it)
}
The IDE gives a type mismatch error.
Actually, if you have only one function to be invoked, I recommend you use Kotlin Callback.
typealias OnDoWorkListener = ((String) -> Unit)
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
}
And in your function, you just set the callback to it
fun main() {
val work = Work()
work.doWork = {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething();
}
We can also use function to set the listener as well.
class Work {
var doWork: OnDoWorkListener? = null
fun doSomething() {
doWork?.invoke("Message Here")
}
fun setOnWorkListener(listener: OnDoWorkListener) {
doWork = listener
}
}
fun main() {
val work = Work()
work.setOnWorkListener {
Log.d("WORK", "This gets called from the `work` object. Message: $it")
}
work.doSomething()
}
Higher order functions make this work:
Kotlin functions are first-class, which means that they can be stored
in variables and data structures, passed as arguments to and returned
from other higher-order functions. You can operate with functions in
any way that is possible for other non-function values.
From the same page:
Passing a lambda to the last parameter
In Kotlin, there is a convention that if the last parameter of a
function accepts a function, a lambda expression that is passed as the
corresponding argument can be placed outside the parentheses:
val product = items.fold(1) { acc, e -> acc * e }
If the lambda is the only argument to that call, the parentheses can
be omitted entirely:
run { println("...") }
Knowing this, a possible update on your class would look like:
class CopyPaste {
private var listener: (String) -> Unit = {}
fun setOnCopyPasteClickListener(onCopyPasteClickListener: (String) -> Unit) {
listener = onCopyPasteClickListener
}
fun doCopyPaste(value: String) {
listener.invoke(value)
}
}
fun main() {
val copyPaste = CopyPaste()
copyPaste.setOnCopyPasteClickListener { println(it) }
copyPaste.doCopyPaste("ClipboardContent!")
}
The class CopyPaste stores the listener, which is a function that takes a String parameter and does not return anything. Its function setOnCopyPasteClickListener accepts a function with the same signature as the listener property and at the end doCopyPaste accepts a String parameter and passes it to the stored function.
Actually, just after I posted, I searched for more thoughts and found this thread: https://youtrack.jetbrains.com/issue/KT-7770 This is indeed a debated limitation as it currently only applies to Java, not Kotlin itself. There is also a suggestion there that gives almost the required simplicity:
interface OnCopyPasteClickListener {
fun onPasteClick(text: String)
companion object {
inline operator fun invoke(crossinline op: (text: String) -> Unit) =
object : OnCopyPasteClickListener {
override fun onPasteClick(text: String) = op(text)
}
}
}
and then, thanks to this overloaded operator, it can be called as:
copypaste.setOnCopyPasteClickListener(CopyPasteMenu.OnCopyPasteClickListener { text ->
do(text)
})
But as the suggested answers offer a more idiomatic solution, I'll accept one of those, I only wanted to include this approach here for reference.