This question already has answers here:
Passing lambda instead of interface
(5 answers)
Closed 1 year ago.
I am new to Android development. Recently I am learning Kotlin and I am trying to figure out setOnClickListener. However, I encountered a problem in the process of converting object expression to lambda using Kotlin.
step 1. setOnClickListener in Java:
buttonLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// my code
}
});
step 2. then I convert Java code to Kotlin code using object expression:
buttonLogin.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
// my code
}
})
step 3. then IntelliJ prompts me to convert object expression to lambda:
buttonLogin.setOnClickListener {
// my code
}
It looks more concise, however, I can't understand the logic behind step 3.
So I checked some information online, it says
Any function that receives an interface with a single function can be
substituted by a lambda
It does on setOnClickListener.
But I still can't fully understand, so I defined an interface and class to verify it.
Here is my code:
interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2(object : MyInterface {
override fun method1() {
println("Hello, world.")
}
})
// So how to write the lambda to replace object expression?
}
The code in step 3 is called trailing lambdas
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses,
If the lambda is the only argument in that call, the parentheses can be omitted entirely:
For example:
fun function(f : (String) -> Unit) {
}
fun main() {
function {
}
}
Check the Kotlin Documentation for this feature
https://kotlinlang.org/docs/lambdas.html#passing-trailing-lambdas
You can convert your code to use this feature
class MyClass {
fun method2(function: () -> Unit) {
function()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
// Or you can store the lambda in variable and use it like tihs
val myClass2 = MyClass()
val function = {
println("Hello, world.")
}
myClass2.method2(function)
}
Or just add convert your interface to a functional interface
fun interface MyInterface {
fun method1()
}
class MyClass {
fun method2(myInterface: MyInterface) {
myInterface.method1()
}
}
fun main() {
val myClass = MyClass()
myClass.method2 {
println("Hello, world.")
}
}
Related
I'm kind of new with mockito so I don't know if this behavior is normal or not.
This is a simple example class:
open class Example( val example2: Example2) {
fun getStuff(fileName: String) : String {
return example2.getFileExtension(fileName)
}
}
open class Example2(val fileUtils: FileUtils) {
fun getFileExtension(fileName: String): String {
return fileUtils.getExtension(fileName)
}
}
So when I tried to test it with this code:
class ExampleTest {
lateinit var example: Example
val example2 = mock(Example2::class.java)
#Before
fun init() {
example = Example(example2)
}
#Test
fun getFileExtensionTest() {
val resultExpected = "jpg"
`when`(example2.getFileExtension(ArgumentMatchers.anyString())).thenReturn("jpg")
assertThat(example.getStuff("hello.jpg"), `is`(resultExpected))
}
}
I'm getting a NullPointerException in return fileUtils.getExtension(fileName) when 'when' is executed
So 'when' is executing the function that suppose to be mocked.
I got it!!
Kotlin makes a function final by default and mockito can't mock final functions/classes. So there are two solutions here:
Add open to the functions
Or make mockito use the final classes and functions : Mockito 2 now supports final
I successfully did some tests of asynchronous function with only one callback interface as parameter with mockito-kotlin library but when I try to do a test of same function with another parameter like a String or Integer I receive error:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.example.presentation.presenter.MyCollectionPresenterTest.getComicListByHeroOK(MyCollectionPresenterTest.kt:97)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
I´m sure I´m mocking properly callback interface with any() but I don´t know if I´m mocking integer parameter correctly. I tried with any(), anyInt(), eq(1) and any() as Int but always the same error.
Here is the class that I want to test:
#PerFragment
class MyCollectionPresenter #Inject constructor(private val useCase: GetComicListByHeroUseCase) {
#Inject
lateinit var view: MyCollectionView
lateinit var models: List<ComicModel>
fun getComicListByHero(heroId: Int) {
useCase.execute(heroId, object : HeroUseCase.GetComicListByHeroCallback {
override fun onComicListReceived(comicList: List<Comic>) {
models = ComicModelMapper.toModel(comicList)
view.setItems(models)
}
override fun onError() {
view.showMessage()
}
})
}
}
And this is the test class:
class MyCollectionPresenterTest : UnitTest() {
private lateinit var presenter: MyCollectionPresenter
#Mock
private lateinit var useCase: GetComicListByHeroUseCase
#Mock
private lateinit var view: MyCollectionView
#Before
fun setUp() {
presenter = MyCollectionPresenter(useCase)
initializeView()
}
#Test
fun getComicListByHeroOK() {
setupGetComicsCallbackOK()
presenter.getComicListByHero(any())
verify(presenter.view).setItems(emptyList())
}
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(any())
verify(presenter.view).showMessage()
}
private fun initializeView() {
presenter.view = view
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(any(), any())
}
private fun setupGetComicsCallbackOK() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onComicListReceived(emptyList())
null
}.`when`(useCase).execute(any(), any())
}
}
This is base unit test class:
#RunWith(MockitoJUnitRunner::class)
abstract class UnitTest {
#Suppress("LeakingThis")
#Rule
#JvmField
val injectMocks = InjectMocksRule.create(this#UnitTest)
}
And this is a class for inject mocks rule:
class InjectMocksRule {
companion object {
fun create(testClass: Any) = TestRule { statement, _ ->
MockitoAnnotations.initMocks(testClass)
statement
}
}
}
Thank you very much for your help and excuse my english.
Regards!
UPDATE: I found the solution and posted as answer.
Finally, I know what I was doing wrong. First at all, it.argument[1] because callback is the second parameter of the function that I want to mock the answer.
And the other thing is that I was mocking the parameter of the function that I want to test presenter.getComicListByHero(1).
Here is the revised code:
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(1)
verify(presenter.view).showMessage()
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[1] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(ArgumentMatchers.anyInt(), any())
}
Thank you very much to #voghDev for his help
I would like to use this function in multiple classes:
fun <T> T?.ifNull(function: (T?, s:String) -> Unit) {
}
How can I accomplish this?
This is how I would like to use it:
class A{
fun <T> T?.ifNull(function: (T?, s:String) -> Unit) {
}
}
class B{
constructor(){
val a = A()
//I want to use the function here
}}
If you define an extension function as a member of a class A, that extension function is only usable in the context of A. That means, you can use it inside A directly, of course. From another class B though, it's not directly visible. Kotlin has so called scope functions like with, which may be used for bringing your class into the scope of A. The following demonstrates how the extension function is called inside B:
class B {
init {
with(A()) {
"anything".ifNull { it, s -> }
}
}
}
As an alternative, and this is mostly the recommended approach, you would define extension functions top-level, i.e. in a file directly:
fun <T> T?.ifNull(function: (T?, s: String) -> Unit) {
}
class A {
init {
"anythingA".ifNull { it, s -> }
}
}
class B {
init {
"anythingB".ifNull { it, s -> }
}
}
I have the following bit of code in my HomeActivity to use LiveData.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Show the launch splash screen.
//
this.setContentView(R.layout.activity_home)
this.viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
this.viewModel.getUser().observe(this, Observer { user: User? ->
});
}
While this seems to work, what does the following part mean?
Observer { user: User? ->
}
This must result in an object that conforms to the Observer interface which has
void onChanged (T t)
https://developer.android.com/reference/android/arch/lifecycle/Observer.html
How does
Observer { user: User? ->
}
result in an object with an onChanged method?
I don't know what putting the name of an interface in front of a lambda expression means.
Thanks!
This is called SAM Conversion, a concept that helps interacting with Java Single Abstract Method Interfaces like in your example.
The following creates an implementation of Runnable, where the single abstract method is run():
val runnable = Runnable { println("This runs in a runnable") }
It’s described in the docs: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Alternatively, but more verbose, would be to use an object:
val runnable2 = object : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
Both are examples of anonymous implementations of that interface. It's of course also possible to create a concrete subclass and instantiate it then.
class MyRunnable : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
val runnable3 = MyRunnable()
in Kotlin the Observer { } lambda gives you param it, you can rename it as you want and use. by default data will be available with it.something() etc...
JAVA:
... new Observer {
void onChanged(User user){
user.something()
}
}
KOTLIN
... object : Observer<User> {
fun onChanged(user: User){
user.something()
}
}
OR
... Observer {
it.something()
}
you can rename it to whatever you want like
... Observer { myUser ->
myUser.something()
}
To omit the Observer { ... } part just add import androidx.lifecycle.observe and use it like this:
this.viewModel.user.observe(this) { user: User? ->
// ...
}
I want to pass an interface as parameter like this:
class Test {
fun main() {
test({})
// how can I pass here?
}
fun test(handler: Handler) {
// do something
}
interface Handler {
fun onCompleted()
}
}
In Java, I can use anonymous function like test(new Handler() { .......... }), but I can't do this in Kotlin. Anyone know how to do this?
In Kotlin you can do :
test(object: Handler {
override fun onComplete() {
}
})
Or make a property the same way:
val handler = object: Handler {
override fun onComplete() {
}
}
And, somewhere in code:
test(handler)
since your interface has only one function. you can convert it to SAM like this
fun interface Handler {
fun onCompleted()
}
then you can just implement this interface using lambda instead and so reduce the overall written code. this is only possible in v1.4
Attached is an example of how to pass an object by parameter that represents the value of the data type and invoke behaviors using interface inheritance.
fun main() {
val customClass = CustomClass(
object : First {
override fun first() {
super.first()
println("first new impl")
}
override fun second() {
super.second()
println("second new impl")
}
}
)
customClass.first.first()
customClass.first.second()
}
data class CustomClass(val first: First)
interface First: Second {
fun first() {
println("first default impl")
}
}
interface Second {
fun second() {
println("second default impl")
}
}
It is worth mentioning that with super.first() or super.second() the default behavior of the interface is being invoked.
It doesn't make much sense to pass a lamda with an anonymous object as a parameter, lambda: () -> Unit , if what we need is to invoke the functions.
GL
Playground