Search code examples
javagenericsfunctional-programmingfunctional-interface

Java call generic function on generic object


I want to have a method like this

<T> void callFuncOnObj(T obj, Runnable r) {
   obj.r();
   //or
   r.runOnTheInstance(obj);
}

that may be used something like this (ignoring exceptions)

   callFuncOnObj(System.in, InputStream::close);

Obviously, Runnable doesn't support this. Apparently, the type of InputStream::close is just Object, so how can it be used or even cast so that a generic instance may call it?

The most obvious alternative I can think of is accepting a Method and dealing with IllegalArgumentException if it doesn't apply,

void callFuncOnObj(Object obj, Method m) {
   try{
      m.invoke(obj);
   }catch(IllegalArgumentException iae) {
      ...
   }...
}

Is there a good way to do this with generic instances and generic functions on those instances?


Solution

  • There a couple of problems. First, InputStream.close() throws an exception, so it can't be passed as a Runnable. You can either convert the method reference to a lambda containing a try/catch block, or create a custom parallel interface that allows exceptions:

    @FunctionalInterface
    interface CustomRunnable {
        void run() throws Exception;
    }
    

    The other issue is that Runnable doesn't accept any arguments. You can pass an instance method reference instead of passing the object separately:

    void callFuncOnObj(CustomRunnable r) {
       r.run();
    }
    

    Called like this:

    callFuncOnObj(System.in::close);
    

    Or you can accept the object with a Consumer (or in this case, a similar interface that allows exceptions) and combine them like this:

    <T> void callFuncOnObj(T obj, CustomConsumer<T> c) {
       c.accept(obj);
    }
    

    And then your original call would work:

    callFuncOnObj(System.in, InputStream::close);