0

Given: A class with a generic Type T which both extends and implements an interface

class RescheduableRunnableIntent<T extends Intent & Timmed> implements Runnable{
    IntentManager intentManager;
    T intent;
}

How can I specify the same condition for an ArrayList or Hashmap? The following line produces a syntax error

private HashSet<? extends Intent & Timmed> set;
Kilian
  • 1,540
  • 16
  • 28
  • 2
    What exactly is the usecase? Can you move the generic specification up to the class level? – Mureinik Dec 17 '17 at 16:25
  • 1
    A workaround would be to create a class which implements `Intent` and `Timmed` and extend this class in the generic – Courage Dec 17 '17 at 16:29
  • 2
    I think it's `T extends X & Y1`, not `?`. You need to actually use a type variable there. But mostly you would use `T` for some type that had already been declared. – markspace Dec 17 '17 at 16:31
  • I tried to use T instead and any permutation I could think of. As @Oswald suggested creating an abstract class in between solves this issue. I am surprised that there is no way to handle it in another way. The use case is an event system which is supposed to execute tasks (the intents) in a repetitive manner. The delay is not necessarily fixed but may be depended on the day of the week other outside conditions. Therefore this class wraps 2 runables which will get executed one after another (the task and the rescheduling operation) into another runable. – Kilian Dec 17 '17 at 16:44
  • See this [answer](https://stackoverflow.com/a/6643378/6413377). It is quite profound explanation about why _there is no way to handle it in another way._ – pirho Dec 17 '17 at 18:25

1 Answers1

0

I prefer not to use the type wildcards because they are too restrictive. Say for instance, check this code sample:

List<? extends Number> listExtendsNumber = new ArrayList<>(Arrays.asList(1, 2, 3));
Number num = new Integer(1);
listExtendsNumber.add(num); // compile error - you can't add stuff
listExtendsNumber.add(1);   // compile error - you can't add stuff

Sometimes you really want to be this restrictive. However, what I found is that people who use bound wildcard types, they usually want something else.

In your you need to figure out whether you want to accept any type that extends Intent and Timmed, or just one particular type?

As far as I can tell, you have the following options:

1. A Wrapper around Set

You can specify a wrapper that deals with one particular type:

public class TimmedIntentSet<T extends Timmed & Intent> implements Set<T> {


    private Set<T> set = new HashSet<>();

    // consider defining constructors other than the default

    @Override
    public int size() {
        return set.size();
    }

    @Override
    public boolean isEmpty() {
        return set.isEmpty();
    }
    // ... more delegator methods
}

This can be quite flexible. Say for instance, consider the following types:

public interface Timmed {}
public interface Intent {}
public interface TimmedIntent extends Timmed, Intent {}
public class TimmedIntentClass implements TimmedIntent {}
public class TimmedAndIntent1 implements Timmed, Intent {}
public class TimmedAndIntent2 implements Timmed, Intent {}

If you're happy with some type-check warnings, then you can do almost anything with this TimmedIntentSet:

    TimmedIntentSet tis = new TimmedIntentSet<>(); // warning
    tis.add(new TimmedAndIntent1());  // warning
    tis.add(new TimmedAndIntent2());  // warning
    tis.add(new TimmedIntentClass()); // warning
    tis.add(2);                       // compile error

However, if you don't want to @Suppress your warnings, then you'll see more than a handful of restrictions:

    TimmedIntentSet<TimmedIntentClass> tis = new TimmedIntentSet<>();
    tis.add(new TimmedAndIntent1());  // compile error
    tis.add(new TimmedAndIntent2());  // compile error
    tis.add(new TimmedIntentClass()); // cool
    tis.add(2);                       // compile error

2. Add that Set to the class where it's used

You can add a Set into the class you've just specified above. This accepts one particular type:

class RescheduableRunnableIntent<T extends Intent & Timmed> implements Runnable{
    IntentManager intentManager;
    T intent;
    Set<T> intentMap;
}

You can define a new type that's both Timmed and Intent, but that has other kind of restrictions. Using the same types as above, you can't just add any of the Timmed and Intent -s to your set:

Set<TimmedIntent> set = new HashSet<>();
set.add(new TimmedAndIntent1());  // compile error
set.add(new TimmedAndIntent2());  // compile error
set.add(new TimmedIntentClass()); // only this is fine

Wrapping up:

It all boils down to what you want to do? Almost anything is possible, but each choice comes with trade-offs.

Tamas Rev
  • 7,008
  • 5
  • 32
  • 49