I had the following problem: a method should take in a bi-function that takes 2 arguments - one is of type Collection<T>
and the other is T
; the actual function could be actually Collection::remove
or Collection::add
, or a more complex operation; the actual function is used for more than a dozen of collection and there are several types of values and collections in the function.
Initially there was no genericity - there were just Collection<String>
s and elements were String
s so declaring the argument as BiFunction<Collection<String>, String, Boolean>
worked just well:
List<String> idCodes;
void visitElement(Element e,
BiFunction<Collection<String>, String, Boolean> elementOp) {
elementOp.apply(idCodes, e.getIdCode());
}
However then I added other types of collections too, and found that I could no longer find out how to use BiFunction
generically:
List<String> idCodes;
List<Integer> weights;
void visitElement(Element e,
BiFunction<...> elementOp) {
elementOp.apply(idCodes, e.getIdCode());
elementOp.apply(weights, e.getWeight());
}
but failed - I could just get compile errors in one place or another no matter what I used for type parameters it would fail.
One of my attempts was
<T> void visitElement(Element e,
BiFunction<Collection<T>, T, Boolean> elementOp)
would fail not when passing in Collection::add
but when actually applying the function to Collection<String>
and String
; or Collection<Integer>
and int
.
Then I made another interface:
interface ElementOp {
<T> boolean apply(Collection<T> collection, T item);
}
And not so surprisingly this now works exactly as I wanted. Therefore my question is:
Do I really have to use
interface ElementOp {
<T> boolean apply(Collection<T> collection, T item);
}
void visitElement(Element e,
ElementOp elementOp) {
elementOp.apply(idCodes, e.getIdCode());
elementOp.apply(weights, e.getWeight());
}
or would it be somehow possible to use BiFunction
for this case?
P.S. I am using Eclipse 4.3 Mars Java compiler, so it might just be that this might not work because of a some bug there.
You could not use a BiFunction
with T
generic that handles both cases (String
and Integer
).
This code could not compile :
<T> void visitElement(Element e,
BiFunction<Collection<T>, T, Boolean> elementOp) {
...
elementOp.apply(idCodes, e.getIdCode());
elementOp.apply(weights, e.getWeight());
}
as BiFunction
is a generic class and you parameterized it with Collection<T>
and T
as function arguments.
So you can only pass Collection<T>
and T
objects/variables while you pass
Collection<String>
and String
in the fist call and Collection<Integer>
and Integer
in the second one.
With this custom interface, things are different :
interface ElementOp {
<T> boolean apply(Collection<T> collection, T item);
}
This works :
elementOp.apply(idCodes, e.getIdCode());
elementOp.apply(weights, e.getWeight());
as contrary to BiFunction
it may accept as parameters any variable declared with any class.
The thing to retain is that ElementOp
is not a generic class.
T
is indeed only a method scope generic that infers the type of the types of the passed arguments.
To address your requirement : invoking multiple times the same method (Collection.add()
or Collection.remove()
) but with args of different types (String
or Integer
), you don't want to use a generic BiFunction<T,Collection<T>, Boolean>
.
The custom functional interface you introduced suits much better.