Al Sweetman
Al Sweetman

Reputation: 945

Java Parameter and return types for enum that implements interface

After much searching I've found lots of relevant questions to normal classes, but none that really relate to enums.

This works fine:

package list;

public interface Testing { 
    //treat as a tag interface.
}
package list;

public class Foo implements Testing {
}
package list;

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

public class Bar {
    public Bar(){
        List<Foo> myList = new ArrayList<Foo>();
        myList.add(new Foo());
        testList(myList);
        testMethod(new Foo());
    }

    public void testMethod(Testing myInstance){
        //do nothing
    }

    public <Testing> void testList(List<Testing> myList){
        //do nothing
    }
}

However, my problem comes in the understanding of what happens when I try and do the same with Enums instead of Foo

The example here is there is an interface definition that is defined as accepting an interface parameter which, in reality, is implemented by an enum that implements the interface:

I have two interfaces:

public interface MyEnumInterface {
  //implemented by Enums
}
import java.util.List;

public interface SecondInterface {
    public MyEnumInterface testMe(String myString);
    public <MyEnumInterface> List<MyEnumInterface> testingList();
    public List <MyEnumInterface> enumTestOne(String anotherString);
    public List <MyEnumInterface> enumTestTwo(String anotherString);
}

Two emum as follows:

public enum MyEnum implements MyEnumInterface{
    VALUE_ONE,
    VALUE_TWO,
    VALUE_THREE;
}
public enum MyEnum2 implements MyEnumInterface{
    VALUE_A,
}

And a test java class:

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

class MyClass implements SecondInterface{

    public MyClass(){
        single(testMe(""));
        enumTestOne("");
        enumTestTwo("");
    }
    public MyEnum testMe(String someParam){
            return MyEnum.VALUE_ONE;
    }

    public List<MyEnumInterface> enumTestOne(String anotherString) {
        List<MyEnum> returnL = new ArrayList<MyEnum>();
        returnL.add(MyEnum.VALUE_ONE);
        returnL.add(MyEnum.VALUE_THREE);
        return returnL;
    }

    public List<MyEnumInterface> enumTestTwo(String anotherString) {
        List<MyEnum2> returnL = new ArrayList<MyEnum2>();
        returnL.add(MyEnum2.VALUE_A);
        return returnL;
    }


    public List<MyEnum> testingList(){
        List<MyEnum> returnL = new ArrayList<MyEnum>();
        returnL.add(MyEnum.VALUE_ONE);
        returnL.add(MyEnum.VALUE_THREE);
        return returnL;
    }


    public void single(MyEnumInterface val){
        System.out.println("Value is " + val);
    }


    public static void main(String[] args){
        MyClass clazz = new MyClass();
    }
}

I'm battling with the problem that the return type of enumTestOne(..) is coded as List but the interface specifies a List. It works fine when it's an individual instance of the Enum 'MyEnum' but when it's a list of values it seems to fail with a:

"error: method listing in class MyClass cannot be applied to given types; required: List found: List reason: actual argument List cannot be converted to List by method invocation conversion"

I'm not sure of a way around this - I've tried adding the declaration you see above so that it reads public <MyEnumInterface> List<MyEnumInterface> testingList(); but that then gives me "warnings" about unchecked casts whilst continuing to fail

This all stems from my lack of deeper understanding around Generics and how they relate to Enums.

I've seen some interesting declarations with respect to <? extends Enum<E> & SecondInterface> but that stretches my understanding. Please help!

As an aside, am I correct in thinking that the generic declarations before the return class are used to specify "T" for type erosion purposes?

Upvotes: 4

Views: 2777

Answers (2)

andersschuller
andersschuller

Reputation: 13907

If you want to allow the implementing class to define which implementation of MyEnumInterface to use, you can specify the return type of the interface methods as a List of any MyEnumInterface.

public List<? extends MyEnumInterface> enumTestOne(String anotherString);
public List<? extends MyEnumInterface> enumTestTwo(String anotherString);

The implementations of these methods can then define the concrete return type.

public List<MyEnum> enumTestOne(String anotherString) {
    List<MyEnum> returnL = new ArrayList<MyEnum>();
    returnL.add(MyEnum.VALUE_ONE);
    returnL.add(MyEnum.VALUE_THREE);
    return returnL;
}

public List<MyEnum2> enumTestTwo(String anotherString) {
    List<MyEnum2> returnL = new ArrayList<MyEnum2>();
    returnL.add(MyEnum2.VALUE_A);
    return returnL;
}

By the way, you can do the same in "reverse" with super. This specifies that any type that is a supertype of the given type may be used.

public void addToList(List<? super MyEnum> list) {
    list.add(MyEnum.VALUE_ONE);
}

addToList(new ArrayList<MyEnum>());
addToList(new ArrayList<MyEnumInterface>());

See also

Java Generics (Wildcards)

Why is SomeClass not equivalent to SomeClass in Java generic types?

Upvotes: 2

Amit Deshpande
Amit Deshpande

Reputation: 19185

Generics are invariant in nature. So List<String> is not subtype of List<Object>

You will have to change your method to include List of MyEnumInterface rather than separate enum classes.

public List<MyEnumInterface> enumTestOne(String anotherString) {
    List<MyEnumInterface> returnL = new ArrayList<MyEnumInterface>();//Change
    returnL.add(MyEnum.VALUE_ONE);
    returnL.add(MyEnum.VALUE_THREE);
    return returnL;
}

public List<MyEnumInterface> enumTestTwo(String anotherString) {
    List<MyEnumInterface> returnL = new ArrayList<MyEnumInterface>();//Change
    returnL.add(MyEnum2.VALUE_A);
    return returnL;
}

Upvotes: 4

Related Questions