Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic parameter type that accepts void

I am trying to create an API for an open source project I am working on, and I have hit a speed bump in trying to extend the API while keeping the semantics consistent with the current API. What I desire is to be able to define a method signature with a generic parameter that accepts the result of calling any method signature. By "any", that is meant to include void methods. I already know that you cannot directly define parameter types of void -- please do not repeat the obvious fact. What is not obvious is whether there is any trick by which a void method call can be provided as an argument to a method (i.e., and ignored).

Back story so this makes a little more sense why I would want to do such a thing, and what my design goal and constraints are, in case the above is impossible (as I fear it is):

My current API defines a very repeatable pattern of methods like this:

public <T,V> Function<T,V> functionFor(V ignoredRetVal) {...}
public <T>   Predicate<T>  predicateFor(V ignoredRetVal) {...}
public <T>   Filter<T>     filterFor(V ignoredRetVal) {...}

As the names imply, the parameters are ignored and are not even used in the implementation. In usage, ignoredRetVal is replaced with a method call to a dynamic proxy. Since parameters are evaluated before the method is invoked, this dynamic proxy method is invoked before the outer function (functionFor or predicateFor, etc.). The dynamic proxy invocation records the Method (or method chain) called, and converts this into a Function object (Guava) or other function-like object from multiple functional libraries.

What I am trying to do now is create a similar semantic that captures method invocations that are used for side-effects only without any need for a return type (such as Functional Java's Effect. If a non-void return type is provided, it is ignored. If a void return type is provided, it too is ignored and accepted. The key is that the semantics must somehow force the proxy method to be invoked before another method that extracts the intercepted proxied method calls. And since we are only interested in side effects, candidate methods are likely to include void methods. Ideally it would look something like:

public <T, V> Effect<T> effectFor(V ignoredRetVal) {...}

(which already works for non-void return types) and it could be used as follows:

Effect<MyClass> effect1 = effectFor (proxyOfMyClass.nonVoidMethod());// OK :-)
Effect<MyClass> effect2 = effectFor (proxyOfMyClass.orVoidMethod()); // Problem!!

As I have said, I'm afraid the semantic I am looking for is not directly supportable. If not, then any alternative should be close in spirit to the pattern I have established. Also, the whole goal of my API was to reduce "vertical noise" of inner class implementations, and I am not a fan of Double Brace Initializers. Whatever suggestions are offered, I am looking for a semantic that supports brevity, especially a single-statement semantic.

like image 801
Kevin Welker Avatar asked Aug 27 '12 04:08

Kevin Welker


1 Answers

I don't think you'll ever be able to coerce a void into an expression, particularly if you don't like the double-brace hack.

You could follow Mockito's example in your API design. Normally, you set up an mock like this:

when(mockedInstance.someMethod()).thenThrow(new IllegalArgumentException());

But for a void, you do this:

doThrow(new IllegalArgumentException()).when(mockedInstance).someMethod();

Similarly, you can enumerate the methods of Effect<T> to make them static methods of your library.

E.g. if Effect<T> has doSomething() then you would invert it, like

doSomething().onEffectFor(proxyInstanceOfA).methodA();

But this assumes that the relevant methods of Effect<T> don't return a value themselves.

If that's not an option, and you need the Effect<T>, you could make it stateful, something like this:

VoidEffect<MyType> effect = effectForVoid(proxyOfMyClass);
effect.on().myVoidMethod();

Where VoidEffect<T> implements Effect<Void>, and on() returns the proxy passed in (or a different proxy). Then you would want to throw an IllegalStateException if on() wasn't called before you otherwise interact with effect.

like image 170
Mark Peters Avatar answered Oct 19 '22 22:10

Mark Peters