Ibolit
Ibolit

Reputation: 9720

Java Generics: Enums and Interfaces

I've read some topics about this, but still can't figure out how I can do the thing I want to do:

I have a number of enums that extend a particular interface (HasCode), which declares one function, code(), which returns the int code of this enum. All the extending enums have private int vars and parameters to their constructors (etc).

I would like to have a function that would accept an enum class as a parameter and return a hashtable of the names of the enum's values and the int values of those enum values.

Here is an example of my enums:

public enum InvoiceStatuses implements HasCode<InvoiceStatuses> {
    NONE(0), REQUESTED(10), RECEIVED(20), APPROVED(30), PAID(40), ERROR(-100);
    private int code;
    private InvoiceStatuses(int a) {
        code = a;
    }
    public int code() {
        return code;
    }
    public static InvoiceStatuses invoiceStatusByCode(int aCode) {
        switch (aCode) {
        case 0:
            return NONE;
        case 10:
            return REQUESTED;
        case 20:
            return RECEIVED;
        case 30:
            return APPROVED;
        case 40:
            return PAID;
        }
        return ERROR;
    }
    public int compare(InvoiceStatuses anotherLevel) {
        if (code > anotherLevel.code()) {
            return 1;
        } else if (code < anotherLevel.code()) {
            return -1;
        }
        return 0;
    }
    @Override
    public InvoiceStatuses[] enumValues() {
        return InvoiceStatuses.values();
    }
}

HasCode:

public interface HasCode <T extends Enum<?>>{
    public int code();
    public T[] enumValues();
}

And here is the class with that magic function (that iterates over the values of the enum)

import java.util.Hashtable;
import java.util.LinkedList;
public class SelectFromEnum&lt;T extends Enum<?>> {
    public SelectFromEnum() {}
    public Hashtable<String, String> createSelect(HasCode<T> anEnum) throws Exception{
        if (! (anEnum instanceof Enum)) {
            throw new Exception ("I only accept enums...");
        }
        T[] values = anEnum.enumValues();
        for (T value : values) {
            System.out.println(value.name() + " __ " + ((HasCode)value).code());
        }
        return null;
    }
}

What i don't like about this:

1) I have to pass a concrete enum member as the parameter to the createSelect function, and I would like to pass the (enum).class;

2) I would like the parameter of the createSelect to specify that it is an Enum that extends a given interface, but i can't quite figure out how to do that.

Update:

In accordance with Ingo Kegel's comment I modified the SelectFromEnum the following way (see below), and now value.name() doesn't compile...

import java.util.Hashtable;
import java.util.LinkedList;

public class SelectFromEnum<C extends HasCode> {
    public SelectFromEnum() {
    }
    public Hashtable&lt;String, String> createSelect(Class<C> anEnum) throws Exception {
        if (!anEnum.isEnum()) { 
            throw new Exception("I only accept enums...");
        }
        C[] values = anEnum.getEnumConstants();
        for (C value : values) {
            System.out.println(value.name() + " __ " + value.code());
        }
        return null;
    }
}

I can add name() to the HasCode interface, but is there a way to persuade the compiler that value is an enum member?

Upvotes: 2

Views: 5236

Answers (2)

ptomli
ptomli

Reputation: 11818

Not sure why you're using a type parameter to HasCode, simply implement the interface on the enums you want to use. I suspect you were trying to use it as a way of restricting implementations to enums, but that shouldn't be necessary. You shouldn't care what implements an interface.

Try it another way

createSelect(EnumSet.allOf(InvoiceStatus.class));

public Hashtable createSelect(EnumSet<? extends HasCode> enums) {
    for (HasCode c : enums) {
        c.code();
    }
}

You're guaranteed that enums will be a Set of Enum values, if that matters to you. Also far simpler to understand, and you don't directly need an instance of enum.

Upvotes: 1

Ingo Kegel
Ingo Kegel

Reputation: 47995

You don't have to use an instance of the enum. You can pass the class and use Class#isEnum and Class#getEnumConstants().

Edit

You can cast to objects returned by Class#getEnumConstants() to Enum and your code should look like this:

public LinkedList createSelectFrom(Class anEnum) throws Exception {
    if (!anEnum.isEnum()) {
        throw new Exception("I only accept enums...");
    }
    Object[] values = anEnum.getEnumConstants();
    for (Object value : values) {
        System.out.println(((Enum)value).name() + " __ " + ((HasCode) value).code());
    }
}

Upvotes: 8

Related Questions