Home > Back-end >  Java Generics - Equality constraint in parameters
Java Generics - Equality constraint in parameters

Time:03-08

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!

CodePudding user response:

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;
}

CodePudding user response:

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.

CodePudding user response:

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>
  • Related