0

Let's say I have a set of sources that supply an array of tagged items where the type of the item varies from source to source. I want to create a default implementation of the source method, but I haven't been able to find a way to do so without getting a compiler warning about unchecked conversions.

To begin with, here's my generic tagged item:

public class TaggedItem<T> {
    private final String tag;
    private final T item;
    public TaggedItem(String tag, T item) {
        this.tag = tag;
        this.item = item;
    }
    public String getTag() { return tag; }
    public T getItem() { return item; }
}

and a sample concrete implementation where the item type is String:

public class TaggedString extends TaggedItem<String> {
    public TaggedString(String tag, String item) {
        super(tag, item);
    }
}

Now here's my generic source interface:

public interface ITaggedItemSource<T> {
    default TaggedItem<T>[] getTaggedItems() {
        return new TaggedItem[0]; // (1)
    }
    default TaggedItem<T>[] getTaggedItems(Class<TaggedItem<T>> clazz) {
        return (TaggedItem<T>[])Array.newInstance(clazz, 0); // (2)
    }
    default List<TaggedItem<T>> getTaggedItemList() {
        return new ArrayList<TaggedItem<T>>();
    }
}

I began with the first method. However this causes the following compiler warning at (1):

Type safety: The expression of type TaggedItem[] needs unchecked conversion to conform to TaggedItem<T>[]

So I added the second method. However this causes the following compiler warning at (2)

Type safety: Unchecked cast from Object to TaggedItem<T>[]

Finally, for the third method, I converted my array to a List and I was warning free.

For the record, here's an example of a concrete source.

public class TaggedStringSource implements ITaggedItemSource<String> {
    @Override
    public TaggedItem<String>[] getTaggedItems() {
        TaggedString[] items = new TaggedString[1];
        items[0] = new TaggedString("t1", "data");
        return items;
    }
    @Override
    public TaggedItem<String>[] getTaggedItems(Class<TaggedItem<String>> clazz) {
        TaggedString[] items = new TaggedString[1];
        items[0] = new TaggedString("t1", "data");
        return items;
    }
    @Override
    public List<TaggedItem<String>> getItemList() {
        List<TaggedItem<String>> items = new ArrayList<>();
        items.add(new TaggedString("t1", "data"));
        return items;
    } 

I should note that both the default array and List implementations work just fine despite the compiler warnings. (As you'd expect I suppose, after all, they're just warnings.)

My questions are:

  1. Is it possible to have a method that uses arrays that does not cause a warning? If so, how?
  2. Why is it possible to avoid a warning when using Lists?
dave
  • 11,641
  • 5
  • 47
  • 65
  • 4
    As a general rule of thumb: arrays and generics should *never* mix. Most importantly, they have different variance models: arrays are always covariant (but sometimes throw exceptions) while generic containers are (usually) always invariant. If you want a generic container, use a `List`, `Collection`, or `Iterable`. (You might need to use arrays in the implementation of a generic container class, and sometimes you can't avoid an unchecked warning in those cases, but I don't think that situation applies here.) – Daniel Pryden Nov 03 '18 at 23:20
  • Thanks @DanielPryden, I'll read up on variance models to make sure I'm clear on them. In this situation I can choose whether to use a collection over an array, so I will do so. – dave Nov 03 '18 at 23:28

0 Answers0