0
votes

Imagine some situation when we need to call some static method with arguments using :: in kotlin. Actually I can do it only with functions without param for example

Base64::hashCode

But when I try to use

Base64::encodeToString

Then I receive a warning that

Overload resolution ambiguity. All these functions match. public open fun encodeToString(input: ByteArray!, flags: Int): String! defined in android.util.Base64 public open fun encodeToString(input: ByteArray!, offset: Int, len: Int, flags: Int): String! defined in android.util.Base64

This form also aren't accepted by compiler

(Base64::encodeToString)( byteArrayOf(2), Base64.DEFAULT)

I wanted to mock this method using new feature of mockito as described here

Thanks for any information!

3
Are you trying to call the method, or get a reference to it? In Kotlin, the :: operator is only use for the latter (so that you can pass the reference to something else which will use it). If you want to call the method, you do so just as you would in Java, with e.g. Base64.encodeToString(…). Or if you want a reference, I'd expect the context to allow it to be disambiguated — if not, perhaps you could post the surrounding code? - gidds
Yes, I need a reference for that method, but I can't do it for method with multiple parameters. - Aleksandr M.

3 Answers

0
votes

The :: syntax is very limited as it won't allow for generic type parameters and methods with multiple parameters. There is nothing stopping you however from passing a lambda reference like this:

{ param ->
    param.encodeToString()
}
0
votes

Generally speaking, callable reference syntax can't be used with overloaded methods, as compiler don't know, what variant you want him to choose (with 2 or 4 parameters in this case). But it is not the only problem here.

Type of the instance obtained in this manner is KFunction, which in parameterful case treated differently than function types (this is a well known, but still unfixed compiler bug).

So, I believe you need to mock both overloaded variants of encodeToString method (if you want to use any of them) with the following syntax:

Mockito.mockStatic(Base64::class.java).use { theMock ->
    theMock.`when`<Any> { Base64.encodeToString(any(), any()) }.thenReturn("MyMockString")
    theMock.`when`<Any> { Base64.encodeToString(any(), any(), any(), any()) }.thenReturn("MyMockString")
    assertEquals("MyMockString", Base64.encodeToString(byteArrayOf(), 111))
    assertEquals("MyMockString", Base64.encodeToString(byteArrayOf(), 111, 111, 111))
}

Also note that Mockito javadocs says:

We recommend against mocking static methods of classes in the standard library

0
votes

You can explicitly specify the type, so it knows which function you want a reference to:

import kotlin.reflect.KFunction2

val encode: KFunction2<ByteArray, Int, ByteArray> = Base64::encode

where KFunction2 means it's a 2-parameter function, and ByteArray and Int are the parameters - the last ByteArray is the return type.

::encode is a static function, so it just deals with the parameters passed in during the call. In contrast, hashcode() is an instance method, so even though the call itself doesn't take a parameter, the function takes a Base64 instance (the thing you're calling hashCode() on):

val hashCode: KFunction1<Base64, Int> = Base64::hashCode

(instance type, return type)

so that's basically how it works - you can define that encode function reference val and call it as usual, and the parameter hints will show you it knows which function it is.

Can't promise it will work for whatever you're doing though!