0

I got a task to rewrite application in Java EE and Java JSF. I decided to go with Spring Boot and Angular. Currently I am struggling with generics in Java.

I have abstract class Item and few classes which extends this class e.g.:

public class Item1 extends Item
public class Item2 extends Item

Then I have an abstract class managing these elements (every item has to be treated differently):

public abstract class ItemAdapter<ELT extends Item> {

    private final Class<ELT> eltClass;

    public boolean isMyElement(Item item) {
        return eltClass.isAssignableFrom(item.getClass());
    }

    @Transactional
    public abstract void add(ELT item);
}

and classes which extends this class:

public class Item1Adapter extends ItemAdapter<Item1>
public class Item2Adapter extends ItemAdapter<Item2>

When I receive List<Item> items I have to perform add method based on its type. So I have auxiliary class:

@Service
@AllArgsConstructor
public class AdapterSetService {
    ...

    public List<ItemAdapter> getAllAdapters() {
        return Arrays.asList(
                item1Adapter,
                item2Adapter,
        );
    }

    public ItemAdapter getAdapterOfType(Item item) {
        for (ItemAdapter adapter : getAllAdapters()) {
            if (adapter.isMyElement(item)) {
                return adapter;
            }
        }

        throw new RuntimeException("Unknown type: " + item);
    }
}

And what I do is:

for (Item item: items) {
    ItemAdapter itemAdapter = adapterSetService.getAdapterOfType(item);
    itemAdapter.add(item);
}

The problem is that I get warnings from IDE:

  1. In AdapterSetService.getAllAdapters method - "Raw use of parameterized class 'ItemAdapter'"
  2. In AdapterSetService.getAdapterOfType method - the same message
  3. When calling adapterSetService.getAdapterOfType - the same message

What I was trying to do:

  1. Changing AdapterSetService.getAllAdapters and AdapterSetService.getAdapterOfType to retun ItemAdapter<? extends Item> but then I get error when calling itemAdapter.add(item) - Required type: capture of ? extends Item Provided: Item

Any suggetions how to solve this?

2
  • In your for-loop in getAdapterOfType you return an Object of the abstract class ItemAdapter. I think that counts as raw use. Commented Nov 29, 2021 at 13:10
  • List<ItemAdapter> — You have defined ItemAdapter to accept a type argument (ELT extends Item). You haven't given one. This is called 'using raw types'. Never use raw types, always provide the type arguments. Commented Nov 29, 2021 at 15:15

1 Answer 1

1

There is no way to safely assume different generic types for elements in a single collection.

I would abandon the generic add method and use runtime type checking instead:

public final void add(Item item) {
    try {
        addImpl(eltClass.cast(item));
    } catch (ClassCastException e) {
        throw new IllegalArgumentException(
            "Expected instance of " + eltClass, e);
    }
}

@Transactional
protected abstract void addImpl(ELT item);

Now you can fix all the raw type occurrences of ItemAdapter. Raw types should never be tolerated in code:

public List<ItemAdapter<? extends Item>> getAllAdapters() {
    return Arrays.asList(
            item1Adapter,
            item2Adapter,
    );
}

public ItemAdapter<? extends Item> getAdapterOfType(Item item) {
    for (ItemAdapter<? extends Item> adapter : getAllAdapters()) {
        // ...
    }
}

And:

for (Item item: items) {
    ItemAdapter<? extends Item> itemAdapter = adapterSetService.getAdapterOfType(item);
    itemAdapter.add(item);
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.