0

Imagine the following class hierarchy:

Cake
\- ChocolateCake
  \- StuffedChocolateCake
\- VanillaCake

I want to write a method to combine Lists of ChocolateCake and VanillaCake, something like:

    public static <T extends Cake> List<T> union(List<T> listA, List<T> listB) {
        List<T> returnObject = new ArrayList<>();
        returnObject.addAll(listA);
        returnObject.addAll(listB);
        return returnObject;
    }

This throws a compilation error I understand:

inferred type does not conform to equality constraint(s)

I tried removing the equality constraint doing something like this:

    public static List<? extends Cake> union(List<? extends Cake> listA, List<? extends Cake> listB) {
        List<? extends Cake> returnObject = new ArrayList<>();
        returnObject.addAll(listA);
        returnObject.addAll(listB);
        return returnObject;
    }

I guess this is a rather basic scenario for Java Generics, what would be the right way of getting this right?

Thanks in advance for your time!

1
  • The T's aren't all the same, so don't use the same letter. What you really need is more like public static List<Cake> union(List<T extends Cake> listA, List<U extends Cake> listB). Commented Mar 8, 2022 at 2:47

3 Answers 3

3

Change the return type to List<Cake> and your second version should work fine:

public static List<Cake> union(List<? extends Cake> listA, List<? extends Cake> listB) {
    List<Cake> returnObject = new ArrayList<>();
    returnObject.addAll(listA);
    returnObject.addAll(listB);
    return returnObject;
}

If you want the option to return some common subtype of Cake, you can combine the wildcard with the generic parameter:

public static <T extends Cake> List<T> union(List<? extends T> listA, List<? extends T> listB) {
    List<T> returnObject = new ArrayList<>();
    returnObject.addAll(listA);
    returnObject.addAll(listB);
    return returnObject;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Tested both approaches and worked fine 👍
2

Your existing code can only union two lists of exactly the same type. However you can change your type signature (the rest of the code can be left unchanged) to

public static <T extends Cake> List<T> union(
    List<? extends T> listA, List<? extends T> listB
)

In that case, you're saying that the two lists can be of any type, as long as that type is a subtype of T, which is a subtype of Cake.

1 Comment

Tested and worked fine 👍
1
package cake;

import java.util.ArrayList;
import java.util.List;

public class Main {


    public static List<Cake> unionB(List<Cake> listA, List<Cake> listB) {
        List<Cake> returnObject = new ArrayList<>();
        returnObject.addAll(listA);
        returnObject.addAll(listB);
        return returnObject;
    }

    public static List<Cake> unionC(List<ChocolateCake> listA, List<VanillaCake> listB) {
        List<Cake> returnObject = new ArrayList<>();
        returnObject.addAll(listA);
        returnObject.addAll(listB);
        return returnObject;
    }

    public static List<Cake> unionD(List<? extends Cake> listA, List<? extends Cake> listB) {
        List<Cake> returnObject = new ArrayList<>();
        returnObject.addAll(listA);
        returnObject.addAll(listB);
        return returnObject;
    }

    public static void setB() {

        List<Cake> a = new ArrayList<>();
        a.add(new ChocolateCake());

        List<Cake> b = new ArrayList<>();
        b.add(new VanillaCake());

        unionB(a, b);
    }

    public static void setC() {

        List<ChocolateCake> a = new ArrayList<>();
        a.add(new ChocolateCake());

        List<VanillaCake> b = new ArrayList<>();
        b.add(new VanillaCake());

        unionC(a, b);
    }

    public static void setD() {

        List<ChocolateCake> a = new ArrayList<>();
        a.add(new ChocolateCake());

        List<VanillaCake> b = new ArrayList<>();
        b.add(new VanillaCake());

        unionD(a, b);
    }
}

This three method is also work.

  1. unionB, unionC, unionD parameter type depends on your real parameter, when we need type like ? extends T, generally there is a container, and the container need keep different type inherit from T. In this case, T is Cake.
  2. ? extends T container can not update, so your second method will cause compile error.
  3. you change the three method return type to List<? extends Cake>

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.