Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Encapsulate class-specific and method-specific generics in one type

Tags:

java

generics

Let's say I need a class that is bounded to a generic Comparable type:

class A<T extends Comparable<T>> {

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }
}

The class has a method with own generics in signature:

<V> Future<V> submit(Callable<V> task) {
    return someExecutorService.submit(task);
}

Now, is there a possibility to restrict the input of the submit method to accept only Callables that also implement T? I first tried this:

<V, W extends T & Callable<V>> Future<V> submit(W task) {
    if(compareSomething(task) != 0)
        throw new RuntimeException("");
    return someExecutorService.submit(task);
}

but found out it's not possible (for reasons described here). Is there any elegant possibility to bypass it?


EDIT: The one ugly possibility I can think about is to split the encapsulation in two different types and pass an object pair in submit:

class A<T extends Comparable<T>> {

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }
    <V> Future<V> submit(Callable<V> task, T comparable) {
        if(compareSomething(comparable) != 0)
            throw new RuntimeException("");
        return someExecutorService.submit(task);
    }
}

The main disadvantage is that the method signature becomes more complicated, also I will need some one-to-one mapping of Ts to Callables for some latter code. Maybe one can suggest a pattern that solves it in an appropriate way?..


EDIT, take two: Let me explain briefly what I'm trying to achieve. I'm working on a custom thread pool implementation that is able to perform some kind of special task scheduling. To do so, this service accepts only one special sort of Callable tasks. Those Callables have to implement a custom interface that is similar to the Comparable one. By comparing pairs of tasks using methods in this interface, the service will:

  1. Block the incoming task if it is blocked by any running task.
  2. Invoke pending tasks on completion of a done task's Future.
  3. Determine the execution order of pending tasks by comparing them.

The blocking/comparison logic should be provided by the tasks themselves. This way the thread pool class should only define what special kind of Comparables the pool object is accepting, and it doesn't care at all what kind of Callables they really are and what is their return type.


EDIT, take three: Based on Erick Robertson's answer, it's now possible to prevent submitting of smelly tasks:

public static void test(String[] args) {
    A<Valid> scheduler = new A<>();
    scheduler.betterSubmit(new Valid()); // applies to method signature
    scheduler.betterSubmit(new Forbidden()); // rejected on compile time
    scheduler.betterSubmit(new ConformWithValid()); // still appliable because all required interfaces implementations recognised
}

// just a bunch of test classes

private static class Valid implements Comparable<Valid>, Callable<Void> {

    @Override
    public int compareTo(Valid o) {
        return 0;
    }

    @Override
    public Void call() throws Exception {
        return null;
    }
}

private static class Forbidden implements Comparable<Forbidden>, Callable<Void> {

    @Override
    public int compareTo(Forbidden o) {
        return -1;
    }

    @Override
    public Void call() throws Exception {
        return null;
    }
}

private static class ConformWithValid implements Comparable<Valid>, Callable<Boolean> {

    @Override
    public int compareTo(Valid o) {
        return 1;
    }

    @Override
    public Boolean call() throws Exception {
        return Boolean.FALSE;
    }
}

Nice and easy! Hope some day this will help someone in the same situation as mine. :-)

like image 597
hoefling Avatar asked Sep 29 '14 09:09

hoefling


People also ask

How can we specify formal type parameter when we define a generic class?

Generic methods have a type parameter (the diamond operator enclosing the type) before the return type of the method declaration. Type parameters can be bounded (we explain bounds later in this article). Generic methods can have different type parameters separated by commas in the method signature.

How can we restrict generics to a subclass of particular class?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

Why do we need generics explain with an example of how generics make a program more flexible?

Generics is a concept in Java where you can enable a class, interface and, method, accept all (reference) types as parameters. In other words it is the concept which enables the users to choose the reference type that a method, constructor of a class accepts, dynamically.

Which of these types Cannot be used to initiate a generic type *?

Which of these types cannot be used to initiate a generic type? Explanation: None.


3 Answers

Use Comparable<T> to constrain the parameter instead of just T.

If the only criteria is that the object is Callable and Comparable, then you can just put those two interfaces on the argument. That even reserves the option of adding a named class to the parameter if this is required to fill another requirement. You'll need to suppress one warning, but it's a safe suppression because you know that T extends Comparable<T>.

public class A<T extends Comparable<T>> {

    // this is just an example of usage of T type
    List<T> comparables;

    ExecutorService someExecutorService = null;

    int compareSomething(T smth) {
        return this.comparables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(Callable<V> task) {
        return this.someExecutorService.submit(task);
    }

    @SuppressWarnings("unchecked")
    <V, W extends Callable<V> & Comparable<T>> Future<V> betterSubmit(W task) {
        if(this.compareSomething((T) task) != 0)
            throw new RuntimeException("");
        return this.someExecutorService.submit(task);
    }
}

I've run into this same problem with another class that I have which references itself as its own generic parameter like this. I don't think it's a clean implementation yet, but I like your example. It provides a nice neat use case which I might be able to pattern some new code after. I generally don't like suppressing warnings, but I don't really mind it since it's only this method which is affected. I hope this will help!

like image 173
Erick Robertson Avatar answered Oct 11 '22 14:10

Erick Robertson


I think Erick Robertson's answer comes closest to a direct solution. I'd like to propose a less direct one (which avoids any unchecked casts). You wrote:

this service accepts only one special sort of Callable tasks. Those Callables have to implement a custom interface that is similar to the Comparable one.

If we actually declare what you described as a type, we come up with this intersection interface:

interface CustomCallable<V> extends Callable<V>, Comparable<CustomCallable<?>> { }

Notice how it implements Comparable<CustomCallable<?>>, not Comparable<CustomCallable<V>>. This is because I'm assuming CustomCallables of different output types should still be comparable to each other by the pool. Otherwise there'd be no point to having V as a method type parameter.

Assuming your pool can accept any type of that intersection interface, we can just remove T entirely and the language limitation is moot:

class A {

    List<CustomCallable<?>> customCallables;

    int compareSomething(CustomCallable<?> smth) {
        return customCallables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(CustomCallable<V> task) {
        if (compareSomething(task) != 0) {
            throw new RuntimeException("");
        }
        return someExecutorService.submit(task);
    }
}

If you insist on parameterizing A on a specific type of custom callable, it gets more complicated. The only way to make it work is to add a "self type" parameter to the intersection interface:

interface CustomCallable<V, SELF> extends Callable<V>, Comparable<CustomCallable<?, ?>> { }

Here, SELF is intended to represent the type of the implementor itself. Note that there's no way for that to be enforced however. More info about self types and their caveats on my answer here: Is there a way to refer to the current type with a type variable?

With the added self type, it's now possible for A to declare a T that submit then insists be used for the self type of the custom callables it's provided:

class A<T extends CustomCallable<?, ?>> {

    List<CustomCallable<?, T>> customCallables;

    int compareSomething(CustomCallable<?, T> smth) {
        return customCallables.get(0).compareTo(smth);
    }

    <V> Future<V> submit(CustomCallable<V, T> task) {
        if (compareSomething(task) != 0) {
            throw new RuntimeException("");
        }
        return someExecutorService.submit(task);
    }
}

Here's an example of a CustomCallable implementation, which resolves the self type:

class MyCustomCallable<V> implements CustomCallable<V, MyCustomCallable<?>> {
    ...
}

Similarly to before, notice how the self type is "relaxed" to MyCustomCallable<?> rather than MyCustomCallable<V> since different output types are interchangeable by design.

And here's a usage example:

A<MyCustomCallable<?>> pool = new A<>();

MyCustomCallable<String> strCallable = ...;
MyCustomCallable<Integer> intCallable = ...;

Future<String> strFuture = pool.submit(strCallable);
Future<Integer> intFuture = pool.submit(intCallable);
like image 2
Paul Bellora Avatar answered Oct 11 '22 13:10

Paul Bellora


I don't see how you can combine both your generic class parameter T and the V in the Callable<V> method argument, for the reasons listed in the answers to the linked post. However, maybe it would be an option - I cannot estimate how counterintuitive that would be, though -- to change the act of submitting a task to a method being invoked on the task itself? Something along the lines of...

class A<T extends Comparable<T> & Callable<?>> {

    public static abstract class Submittable<T extends Submittable<T,V>,V>
      implements Comparable<T>, Callable<V> {
        // ugly, but unavoidable
        @SuppressWarnings("unchecked")
        public Future<V> submitTo(A<? super T> scheduler) {
          return (Future<V>) scheduler.submit((T) this);
        }
    }

    // this is just an example of usage of T type
    List<T> comparables;

    int compareSomething(T smth) {
        return comparables.get(0).compareTo(smth);
    }

    // *can* be left public, but that way you enforce a single way
    // of submitting tasks, namely via submitTo
    private Future<?> submit(T task) {
        if(compareSomething(task) != 0)
            throw new RuntimeException("");
        // the following cast is a save conversion - you could also write it cast-free
        // as Callable<?> callable = task; ...submit(callable);
        return someExecutorService.submit((Callable<?>) task);
    }
}

Now, your tasks all have to inherit A.Submittable, and then can be submitted to a scheduler instance via submitTo, returning a Future<V> for the correct type V.

Of course, this crucially relies on T in fact being a selftype of the subclasses of Submittable. And what's going on in submitTo is - well, ugly. But it does provide you with the convenience of returning a Future<V> of the correct type.

like image 1
misberner Avatar answered Oct 11 '22 13:10

misberner