Reputation: 38541
I have an interface that defines a method that does some computation
public interface Computable {
public Result compute(Foo foo);
}
I want to also pass in a set of arguments to the computation that can be peeled off. I can hack this up, but I am wondering if there's an elegant solution with generics
and var args
. Something like...
public class Parameter<K,V> {
private final K key;
private final V value;
public Parameter(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return this.key;
}
public V getValue() {
return value;
}
}
But then I'm lost as to how going through each parameter from the list of parameters I would be able to simply get the key value pairs with their types inferred. Can someone help me? Has this not already been built into the JDK?
EDIT with example:
In a concrete implementation we'd have....
public Result compute(Foo foo,
Parameter ...parameters) {
// Here I'd like to get each parameter, and have it know it's key type and value type
for(Parameter p : parameters) {
p.getKey();
p.getValue()
//implementers know what to do with their parameters
}
}
Upvotes: 2
Views: 149
Reputation: 12752
If you have different key types for the different parameters or different value types, you probably won't be able to solve this with a simple generic varargs function definition. Your only type-safe option would then be something like this:
public Result compute(Foo foo, Parameter<Byte, Byte> p1, Parameter<Byte, Float> p2, ...)
(here "..." denotes further parameters added in the same way, not the varargs ellipses)
.
If you need to be flexible, though, and you can determine which key and value type you are dealing with by probing some other property of the Parameter, there's a trick I sometimes use to simplify the type-casting: Change your getKey()
function in the Parameter
class to this:
@SuppressWarnings("unchecked")
public <KeyType> KeyType getKey() {
return (KeyType) this.key;
}
(and similarly for the getValue() function)
That way, you can simply assign the results of getKey()
to a variable of any (non-primitive) type, like this:
Integer key = parameter.getKey();
or
String key = parameter.getKey();
and you won't have to do any type casting in your compute function, and there won't be any warnings.
But: You will loose the compile-time type-check! So, you have to make sure at runtime (which you might have to anyway) that you don't assign a key to a variable with a wrong type. Or at least handle the resulting ClassCastException gracefully.
Upvotes: 1
Reputation: 55223
As AmitD mentioned in the comments, you should make Computable
a parameterized type:
public interface Computable<K, V> {
public Result compute(Foo foo, Parameter<K, V>... parameters);
}
An implementation would then resolve K
and V
and the types would be known:
public class BarBazComputable implements Computable<Bar, Baz> {
@Override
public Result compute(Foo foo, Parameter<Bar, Baz>... parameters) {
...
}
}
Edit: And yes, as AmitD mentioned again, you're not required to resolve either or all of the type parameters:
public class WithStringComputable<K> implements Computable<K, String> {
@Override
public Result compute(Foo foo, Parameter<K, String>... parameters) {
...
}
}
public class OhJustACoupleObjectsComputable<K, V> implements Computable<K, V> {
@Override
public Result compute(Foo foo, Parameter<K, V>... parameters) {
...
}
}
public class NumbersComputable<N1 extends Number, N2 extends Number> implements Computable<N1, N2> {
@Override
public Result compute(Foo foo, Parameter<N1, N2>... parameters) {
...
}
}
Note that varargs don't play nice with generics, and you will get a type-safety warning at the callsite when invoking compute
. Instead, consider having the method take an Iterable<Parameter<K, V>>
.
Upvotes: 3