2

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
    }


}
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124

2 Answers2

3

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>>.

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
1

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.

Markus A.
  • 12,349
  • 8
  • 52
  • 116
  • or if you know for sure that your key is a subtype of some other type, like K in your example, you can get at least that bit of type-safety by using: public KeyType getKey() {... – Markus A. Nov 08 '12 at 06:56
  • What does the KeyType class look like? – Amir Afghani Nov 08 '12 at 15:10
  • @AmirAfghani It's not a class. It's simply a placeholder that says "whatever you assign it to" – Markus A. Nov 08 '12 at 15:41
  • It's a generic function defintion. You could for example do this: public MyNum add(MyNum x, MyNum y) ... That enforces that you pass the same number type for x and y and says that the return type is also the same, but you don't need to say beforehand what MyNum is. – Markus A. Nov 08 '12 at 15:42
  • @AmirAfghani No worries, it took me a sec to get that the first time I saw it as well... BTW: There is one case where the compiler isn't smart enough to treat this 100% right and will sometimes throw an exception: If you have a generic function that calls another generic function, it sometimes doesn't resolve the "indirection" right. See here for more info: http://stackoverflow.com/questions/5666027/why-does-the-compiler-state-no-unique-maximal-instance-exists. Cheers. – Markus A. Nov 09 '12 at 17:43