2
public class EnumFromInt<TEnum extends Enum<TEnum>> {

    private static TEnum[] _values;

    private static TEnum[] GetValues() {
        if (_values != null)
        {
            return _values;
        }

        _values = TEnum.values();
        return _values;
    }
}

Above, there are two instances of "cannot be referenced from a static context". They seem separate, so I'll treat them as different problems. I'd like to understand both.

  • TEnum[] - Why? It's an array of whatever type TEnum is. (Well, that's what I'd like it to be)
  • TEnum.values() - TEnum is declared to be of type Enum, so shouldn't I be able to use the static functions that belong to all enums?
Tristan
  • 1,388
  • 1
  • 14
  • 24
  • 6
    Class-level generics only apply to instances in Java, so you can't reference them in a static context. And you can't call static methods on a generic type. I don't know the reasoning behind the latter, but here's a related question: https://stackoverflow.com/q/6512179/1553851 – shmosel Dec 05 '17 at 23:17
  • Your two bullet points are inconsistent with each other about the nature of `TEnum`. The first is more correct with its "whatever type TEnum is". The second seems to suppose that `TEnum` is an object "declared to be *of* type Enum" (emphasis added), which it is not. – John Bollinger Dec 05 '17 at 23:21
  • 1
    @JohnBollinger But it is: `TEnum extends Enum` – shmosel Dec 05 '17 at 23:22
  • With respect to the first bullet point, see [What's the reason I can't create generic array types in Java?](https://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java). – John Bollinger Dec 05 '17 at 23:23
  • 1
    No, @shmosel, `TEnum` is a type parameter, representing a type. That it is declared with a bound does not make it an object or the identifier of an object. – John Bollinger Dec 05 '17 at 23:25
  • 1
    @JohnBollinger I see, I misread your comment. Still, given that `TEnum` represents a type, it's reasonable to expect to be able to call a static method on said type. – shmosel Dec 05 '17 at 23:26
  • 1
    @JohnBollinger As far as generic type arrays are concerned, `TEnum[]` is a perfectly valid type in a non-static context. – shmosel Dec 05 '17 at 23:27
  • You are right, @shmosel. – John Bollinger Dec 05 '17 at 23:37
  • Thank you. The above discussion is helpful. – Tristan Dec 05 '17 at 23:42
  • 1
    Please just call the generic type `T` and not `TEnum`. TEnum looks like a normal type, not a generic type. If you use `T' it is clear that you mean the generic type. – NickL Dec 05 '17 at 23:53
  • @NickL I want the 'extends' constraint to be obvious to maintenance coders. – Tristan Dec 05 '17 at 23:56
  • 3
    Look [here](https://docs.oracle.com/javase/tutorial/java/generics/types.html) for the Java Generic type naming convention. "Without this convention, it would be difficult to tell the difference between a type variable and an ordinary class or interface name." – NickL Dec 05 '17 at 23:58
  • I don't really see the point of this code. What is the end goal? (The hypothetical) `MyEnum[] vals = EnumFromInt.GetValues()`? Why not just use `MyEnum[] vals = MyEnum.values()` directly then? – Jorn Vernee Dec 06 '17 at 01:36
  • The sample above is incomplete. We have a C++ layer that is called by Java. Code gen creates enum code in both languages. Getting a Java enum from an int (the C++ enum) is common enough that I'd like to generalize it. _values() is slightly expensive due to data copying, so I'd like to cache it. Any part of our Java code might want to do such a conversion, so I'd like the entry point to be static. – Tristan Dec 06 '17 at 22:13
  • The missing function is: public static TEnum FromInt(int codeGenInt) { return GetValues()[codeGenInt]; } – Tristan Dec 06 '17 at 22:17

1 Answers1

1

From your comment I've got an idea what you want to do.

In that case you would need to pass the enum's class and call getEnumConstants:

class EnumFromInt {

    private static final Map<Class<?>, Object[]> _values = new HashMap<>();

    @SuppressWarnings("unchecked")
    private static <TEnum extends Enum<TEnum>> TEnum[] getValues(Class<TEnum> cls) {
        return (TEnum[]) _values.computeIfAbsent(cls, Class::getEnumConstants);
    }
}

You'd also have to use a Map, since a field could only store 1 array of values. There's no way to enforce that the type of the key correspond to the type of the value, so you need to store the value arrays as Object[] and use an unchecked cast later when retrieving them.

Jorn Vernee
  • 26,917
  • 3
  • 67
  • 80