3

I have a generic interface defined as:

public interface ItemService<T extends Slicer, E extends ItemSlice> {
    E getItemHistory(final String id, final T slicer);
}

And an implementation:

public class DynamoDbItemService implements ItemService<DynamoDbSlicer, DynamoDbItemSlice> {
    public DynamoDbItemSlice getItemHistory(final String id, final DynamoDbSlicer slicer) {
    }
}

Here are the definitions of the four classes referenced above:

public interface Slicer {
    Map<String, ? extends Serializable> pointer();
}

public class DynamoDbSlicer implements Slicer {
    public Map<String, AttributeValue> pointer() {
    }
}

public interface ItemSlice extends Slice<Item> {
}

public interface Slice<T> {
    List<T> data();
    Slicer next();
}

public class DynamoDbItemSlice implements ItemSlice {
    publi ImmutableList<Item> data() {}
    public DynamoDbSlicer next() {}
}

I would like to reference the ItemService interface but for it to be bound to the DynamoDbItemService implementation so I can swap it out if necessary which I can do like so:

ItemService<? extends Slicer, ? extends ItemSlice> itemService = new DynamoDbItemService itemService();

but if I try to use itemService like this:

ItemSlice slice = itemService.getItemHistory(itemId, DynamoDbSlicer.first(1));
slice = itemService.getItemHistory(itemId, slice.next());

I get these two compilation errors:

Error:(231, 82) java: incompatible types: item.pagination.DynamoDbSlicer cannot be converted to capture#1 of ? extends pagination.Slicer

Error:(238, 62) java: incompatible types: pagination.Slicer cannot be converted to capture#2 of ? extends pagination.Slicer

I understand from this question that ? wildcards cannot be identical so my question is - can I do what I want - work with the interface? If so, how? Or am I approaching this incorrectly?

I have asked two previous questions related to this which have helped along the way (first and second)

Stuart Leyland-Cole
  • 1,243
  • 7
  • 19
  • 35
  • 1
    It would be nice if we could get all the relevant methods so we can debug this. You are missing quite a few methods in there, most notably `DynamoDbSlicer.first()`. – Matthew May 30 '19 at 09:39

1 Answers1

1

I don't see a reason why the following wouldn't work for you:

ItemService<DynamoDbSlicer, DynamoDbItemSlice> itemService = new DynamoDbItemService();
ItemSlice slice = itemService.getItemHistory("",  new DynamoDbSlicer());

But if you would like to make the code as modular as possible you can use this method to perform an unchecked cast (pure evil some say) and get the result you want:

public static void main(String[] args) {

    ItemSlice slice = getMySlice(new DynamoDbItemService());
}

@SuppressWarnings("unchecked")
public static <T extends Slicer, E extends ItemSlice> E getMySlice(ItemService<T, E> service) {
    return service.getItemHistory("", (T) new DynamoDbSlicer());
}

And then of course there is the solution of passing the type inference responsibility to the actual field that is storing the value. I myself would go for this solution, as I think it offers the most flexibility:

public class DynamoDbItemService<T extends Slicer, E extends ItemSlice> implements ItemService<T, E>

ItemService<DynamoDbSlicer, DynamoDbItemSlice> itemService = new DynamoDbItemService();
ItemSlice slice = itemService.getItemHistory("", new DynamoDbSlicer());
Matthew
  • 1,905
  • 3
  • 19
  • 26