Any::hashCode function type mismatch

72 Views Asked by At
fun myHashcode() : Int{
        return 1
}

val functionRef1: () -> Int = Any::hashCode
val functionRef2: () -> Int = ::myHashcode

why Any::hashCode is not assigned in functionRef1 with error: Type mismatch. Required: () → Int Found: KFunction1<Any, Int>

but when i change the code like this(this is suggested by the IDE):

val functionRef1: (Any) -> Int = Any::hashCode then everything compiles. Where does this Any parameter come from if it is not in the signature of the hashCode method in the Any class?

of course I know what can be done like this val functionRef1: KFunction1<Unit, Int> = Any::hashCode but I'm more interested in signature (Any) -> Int

1

There are 1 best solutions below

2
Raphael Tarita On

The correct function type signature for your myHashCode is, as you correctly put it, () -> Int. It follows the general construction rule of function type signatures in Kotlin, which is:

<Receiver>.(<Parameters>) -> <ReturnType>

When we look at your function:

fun myHashcode(): Int {
    return 1
}

It is clear that:

  1. The function has no receiver type (it is neither a member function, nor an extension function)
  2. The function takes no parameters
  3. The function returns Int

When we put this all together, we get the previously mentioned function type signature.

Now, let's look at kotlin.Any#hashCode (source):

// ...
public open class Any {
    // ...
    public open fun hashCode(): Int
}

Going through the signature of this function:

  1. Because hashCode is a member of Any, the function has a receiver type of Any
  2. The function takes no parameters
  3. The function returns Int

Which means, according to the construction rule, the correct function type for Any::hashCode must be Any.() -> Int.

But, as IntelliJ mentioned to you, (Any) -> Int also seems to be a correct signature for this function. That is because function type signatures in Kotlin are interchangeable with their desugared counterparts.

The whole "receiver type" thing in Kotlin is just syntactic sugar, because on the JVM(*) there are no receiver types. Instead, extension functions on the JVM are really just functions that take one extra parameter.

And even member functions (like kotlin.Any#hashCode) treat their instance as a parameter when used as method references. This fact comes from Java, not Kotlin:

public final class Foo {
    public static int fooParameter(Foo param) { 
        // ...
    }
    
    public int memberOfFoo() {
        // ...
    }
}

// ...

Function<Foo, Integer> first = Foo::fooParameter;
Function<Foo, Integer> second = Foo::memberOfFoo;

As you can see, the function type signature in Java does not discriminate between the this param and any arbitrary param, and the same goes for Kotlin, with the difference that Kotlin does have a syntactic feature to declare receiver types for function signatures. But you don't have to use it.

Meaning, that Any.() -> Int and (Any) -> Int are basically the same, or specifically, these two are both valid function signature types for the hashCode() member function of Any.

(*) The subsequently described behaviour of receiver/parameter interchangeability is most likely caused by Kotlin having been originally developed as a JVM language. However, as of today, Kotlin supports more platforms than just the JVM, namely Android, JS, Native and WASM (experimental). The described behaviour applies to all targets equally.