1

I'm implementing a special priority queue. The implementation protects the value of queued items from being changed while being queued. It's a priority queue optimized for a certain use case which needs that invariant. Doing that, I ran into a problem concerning java inheritance and type.

The implementation consists of an interface defining the queue and a class defining a queued item. The latter should be extended by a class for concrete objects to be put into the queue.

The queue interface:

public interface PriorityQueueInterface<T extends Queueable> {
    void add(T elem);
    boolean remove(T element);
    T poll();
}

The queueable item class:

public class Queueable {
    private PriorityQueueInterface<? extends Queueable> queue;
    private boolean queued = false;
    private double value;
    public Queueable(PriorityQueueInterface<Queueable> queue) { this.queue = queue; }

    public void setValue(double value) {
        if (queued) throw new RuntimeException("can't set value while queued");
        this.value = value;
    }

    public double getValue() { return value; }

    public boolean queue() {
        if (queued) return false;
        queue.add(this);
        queued = true;
        return true;
    }

    public boolean unqueue() {
        if(!queued) return false;
        queue.remove(this);
        queued = false;
        return true;
    }
}

Sample use of Queueable:

public class QueueableTestItem extends Queueable {
    public QueueableTestItem(PriorityQueueInterface<QueueableTestItem> queue) {
        super(queue);
    }
}

I'm obviously doing something wrong here, the error is:

The call queue.add(this) in Queueable.queue is invalid, as type ? extends Queueable is expected while Queueable is provided.

I want to define the controlled queueing and unqueueing into the Queueable class though - how do I set the types correctly to make this work?

If the reasons for choosing this way are not obvious from the example, it's because I simplified it for providing a minimal example. It's a cutout from a much bigger context.

UPDATE

This is why I think, that How can I add to List<? extends Number> data structures? doesn't answer my question:

I understand, that I cannot add to a collection of a generic type (as described in that post).

When extending Queueable to e.g. QueueableTestItem then QueueableTestItem.queue is supposed to hold only elements of type QueueableTestItem.

The class Queueable exists to define the mechanics of using the queue, but it's not meant to be used as is - it's meant to be extended.

So the question is: what's the correct java syntax for that concept?

  • 2
    Does this answer your question? [How can I add to List extends Number> data structures?](https://stackoverflow.com/questions/2776975/how-can-i-add-to-list-extends-number-data-structures) – Joe Feb 10 '22 at 12:45
  • 1
    Yes and no. I understand, why it doesn't work in the way I wrote it. What I want, though, should be possible: A priority queue instance is to hold only one clearly defined type of objects (that's the difference to the example you gave). The queue interface defines it like that. The question is: how to I write the `Queueable` class, so when extended by another class it works with a priority queue of the specific type of the extending class? – Florian Metzger-Noel Feb 10 '22 at 12:53
  • When I change `Queueable` to `private PriorityQueueInterface queue;` then `Queueable` works - but then `QueueableTestItem` has a type error. – Florian Metzger-Noel Feb 10 '22 at 12:56
  • 1
    It is very rare that you really need to use a `? extends` clause in your own code. In this case I think the solution is to make `Queueable>` generic, and then the queue is of type `PriorityQueueInterface` and subclasses are declared like `class Foo extends Queueable`. If that solves your problem, then I can write it up as an answer. – kaya3 Feb 10 '22 at 13:40
  • There's no way to do exactly what you want without casting, because there's no way in Java to require a `PriorityQueueInterface`. `PriorityQueueInterface` is probably the appropriate type. – Matt Timmermans Feb 10 '22 at 13:47
  • @kaya3: I've already tried something along this line but couldn't make it work. You're proposition results in error `Required type: T, Provided: Queueable ` for `queue.add(this)` in `Queueable`. – Florian Metzger-Noel Feb 10 '22 at 13:47
  • @MattTimmermans I have a working version using casting - but my IDE as well as SonarQube hate that solution and keep asking me to improve it. If it's not possible without casting, I will live with it. It just seems as there must be a clean solution as all types are known. – Florian Metzger-Noel Feb 10 '22 at 13:50
  • 1
    You can actually make it compile using [Self Type with generics](https://www.sitepoint.com/self-types-with-javas-generics/), but the reader will be crazy. I think cast make sense if you don't want to redesign. The main problem is your solution has circular type reference. – samabcde Feb 10 '22 at 13:57
  • I'm getting the impression, that I should use a simple type cast or rethink my architecture if the implementation would force me to such esoteric means - my concept is obviously not in line with "the java way" of doing this. – Florian Metzger-Noel Feb 10 '22 at 16:10

0 Answers0