mogronalol
mogronalol

Reputation: 3015

Combine class and method parameterized type

I have an interface like this:

public interface BatchSynchronisedPool<R extends Runnable> {
    void execute(R runnable, Object batchIdentifier);
    public <T> Future<T> submit(Callable<T> callable, Object batchIdentifier);
}

I want to infer an upper bound for Callable, but still want to be able to keep the <T> argument on the method:

public interface BatchSynchronisedPool<R extends Runnable, C extends Callable> {
    void execute(R runnable, Object batchIdentifier);
    public <T> Future<T> submit(C<T> callable, Object batchIdentifier);
}

Obviously that doesn't work, because the type of C I specify may only take a specific range of T arguments. However, now you have the general just of what I am trying to do, is there a possible solution, or am I stuck always having to submit a callable? (Or remove generics altogether and perform an unsafe cast)

Upvotes: 4

Views: 193

Answers (1)

Jon Newmuis
Jon Newmuis

Reputation: 26520

I'm not 100% certain, but don't think that you can do what you're trying to do here. Since C is not generic, you can't use C<T>. Lots of code below, but tl;dr take option 3. All that really changes in the end is how many BatchSynchronisedPool objects you'll need to create, which is not really a considerable overhead...


1. Keep the <T> generic type parameter on the method, submit a Callable<T> and perform a runtime check of the types, like your original solution, in the class that implements this interface.

public interface BatchSynchronisedPool<R extends Runnable> {
    void execute(R runnable, Object batchIdentifier);
    public <T> Future<T> submit(Callable<T> callable, Object batchIdentifier);
}

public class MyBSP<R, C> implements BatchSynchronisedPool<R, C> {
    void execute(R runnable, Object batchIdentifier) { ... }
    public <T> Future<T> submit(Callable<T> callable, Object batchIdentifier) {
        // Check types.
        if (!(callable instanceof MyDesiredCallableClass)) {
            throw new IllegalArgumentException("Types don't match.");
        }

        // Do work.
        T result = callable.call();

        ...
    }
}

public class MyUsageClass {
     public static void main(String[] args) {
         // Submit string.
         MyBSP<Runnable> bsp = new MyBSP<Runnable>();
         bsp.submit(new StringCallable(), someObject1);

         // Submit integer.
         bsp.submit(new IntegerCallable(), someObject2);
     }
}

2. Keep the <T> generic type parameter on the method, submit a C and perform a cast, like you've suggested, in the class that implements this interface.

public interface BatchSynchronisedPool<R extends Runnable, C extends Callable> {
    void execute(R runnable, Object batchIdentifier);
    public <T> Future<T> submit(Class<T> cls, C callable, Object batchIdentifier);
}

public class MyBSP<R, C> implements BatchSynchronisedPool<R, C> {
    void execute(R runnable, Object batchIdentifier) { ... }
    public <T> Future<T> submit(Class<T> cls, C callable, Object batchIdentifier) {
        // Do work... with a cast.
        T result = cls.cast(callable.call());

        ...
    }
}

public class MyUsageClass {
     public static void main(String[] args) {
         // Submit string.
         MyBSP<Runnable, Callable> bsp = new MyBSP<Runnable, Callable>();
         bsp.submit(new StringCallable(), someObject1);

         // Submit integer.
         bsp.submit(new IntegerCallable(), someObject2);
     }
}

3. Create a new BatchSynchronisedPool for each type T that you're trying to submit by specifying T as a generic type parameter for the class. Then each time you want to call submit on a different type, you'd need to generate a new instance of BatchSynchronisedPool.

public interface BatchSynchronisedPool<T, R extends Runnable, C extends Callable<T>> {
    void execute(R runnable, Object batchIdentifier);
    public Future<T> submit(C callable, Object batchIdentifier);
}

public class MyBSP<T, R, C> implements BatchSynchronisedPool<T, R, C> {
    void execute(R runnable, Object batchIdentifier) { ... }
    public Future<T> submit(C callable, Object batchIdentifier) {
        // Do work.  Types are okay; no checking or casting needed!
        T result = callable.call();

        ...
    }
}

public class MyUsageClass {
     public static void main(String[] args) {
         // Submit string.
         MyBSP<String, Runnable, Callable<String>> stringBsp = new MyBSP<String, Runnable, Callable<String>>();
         stringBsp.submit(new StringCallable(), someObject1);

         // Submit integer.
         MyBSP<Integer, Runnable, Callable<Integer>> integerBsp = new MyBSP<Integer, Runnable, Callable<Integer>>();
         integerBsp.submit(new IntegerCallable(), someObject2);
     }
}

Upvotes: 4

Related Questions