Just wanna spread some love: KFunction alone is en...
# reflect
c
Just wanna spread some love: KFunction alone is enough of a reason to ditch Java and go all in on Kotlin. We've removed all the pesky string representations of methods from our codebase! from
"some.package.ControllerName.actionMethod"
we now use
ControllerName::actionMethod
: type safe, refactor friendly, less prone to bugs, and just plain stupid that this is still (2024!) not possible in Java. Thanks to whomever got this right in Kotlin, I enjoy my working life on the JVM a lot better since. 💚💛❤️
k
Why can't you use Java method references?
d
I mean, kotlin is wonderful, but you could have done that in Java as well. Even better in both Java and Kotlin, is use a SAM or Lambda instead of KFunction.
c
Why can I not do this then in Java:
Copy code
Map.of(MyClass::stop, "stop method");
Which I can do in Kotlin. (KFunction, I loooooove it) Also, I'm not sure how SAM or Lambda will help me here... @Klitos Kyriacou @Daniel Pitts
d
In java, you have to be a little more explicit with the return type:
Copy code
Map.<Consumer<MyClass>, String>of(MyClass::stop, "stop method");
Otherwise it doesn't know what to treat ::stop as.
Definitely prettier in Kotlin, but not impossible in Java.
c
I get "ConsumerMyClass is not a functional interface" when I put that in a Java project. (thanks btw for taking the time to reply, I appreciate)
d
Hmm, here is my entire file:
Copy code
package com.stochastictinkr;

import java.util.Map;
import java.util.function.Consumer;

public class Examples {
    static class MyClass {
        public void stop() {
            System.out.println("Hello from stop");
        }
    }

    public static void main(String[] args) {
        Map.<Consumer<MyClass>, String>of(MyClass::stop, "stop method");
    }
}
c
I tried really hard in Java, and never amanged.
d
Copy code
Map<Consumer<MyClass>, String> map = Map.of(MyClass::stop, "stop method");
also works.
Make sure the Consumer being imported is
import java.util.function.Consumer;
c
Yes! That works.. Well I should have asked here then 🙂
Now we are already 10)% Kotlin. 🙂
d
Eh, its not really a Java community. I just happen to have a lot of Java experience on top of Kotlin.
c
Or below Kotlin 🙂 I tried to generalize your example to more methods on different classes (which is what I would have needed) but then i run into problems.
d
What did you try?
c
Copy code
import java.util.Map;
import java.util.function.Consumer;

public class Example {

  static class MuhClass extends Example {
    public void something() {
      System.out.println("Hello from stop");
    }
  }

  static class MyClass extends Example {
    public void somethingElse() {
      System.out.println("Hello from stop");
    }
  }

  public void test() {
    Map.<Consumer<Example>, String>of(MyClass::somethingElse, "1", MuhClass::something, "2");
  }
}
Please dont put too much time in this, we already have a KFunction-based solution we're happy with. The Java method refs were not sufficient for what we wanted. I tried (to no avail):
Copy code
Map.<Consumer<?>, String>of(MyClass::somethingElse, "1", MuhClass::something, "2");
d
When you access that map, you would need to provide those methods with an object (in either Java or Kotlin). Using <?> isn’t going to work for that. If you have existing objects, rather than classes, you can use
Runnable
instead, and use the object method reference:
myObj::something
and
muhObj::somethingElse
Also worth noting, that using KFunction directly is a code smell. You should be using a function type.
c
@Daniel Pitts "If you have existing objects" we dont. KFunction works beautifully. I dont get how it can be a code smell, as it has made our code a lot less smelly (I think referring to methods with a string (e.g.
"some.package.OurClass.methodName"
) is a code smell, or better a language smell (a language should facilitate type-safe refs to methods that can be passed as values). Do you know anyplace where I can read more on KFunction being a code smell?
u
The Java example in this thread doesn't really work in practice because Java method references lack equals/hashCode, so if you try to get the value by key in this map you'll get null. And that's in addition to the minor inconvenience of Java method references not having a specific functional type, so you need to write
c.get((Consumer<MyClass>) MyClass::stop)
d
Ah, I think I misunderstood the mapping. You want to look up a description of a method by the method itself, not the other way around.
I mistakenly assumed you were making calls to the methods after finding them in the map. I am curious now what the underlying use-case is. When do you have a reference to a method that you need to find the description of?
c
@Daniel Pitts We have a router that uses references to
package.ControllerClass.actionMethod(...)
as handles for the route. The framework dictates that these references are of type
String
and contain the reference as such: ``package.ControllerClass.actionMethod`` This breaks type-safety: you can change the name of the method and suddenly using the reference will result in an error AT RUNTIME. So we wrote a thin wrapper around it so we can use
KFunction<*>
instead of
String
to refer to these routes. These
KFunction
arguments are translated to
String
before they are fed to the router, nothing changed from the persective of the router. But we now refer to an action method with
ControllerClass::actionMethod
and no refactorings work, CTRL-click works, and everyone is happy. I could not achieve this with Java. Hence we love
KFunction
.