0

I'm trying to define the following anonymous Google Guava Function instance:

Function<E extends Enum<E>, String> ENUM_TO_STRING = new Function<E extends Enum<E>, String>() {
    @Override
    public String apply(E enumValue) {
        String result = null;
        if (enumValue != null) {
            result = enumValue.toString();
        }
        return result;
    }
};

The idea is a simple transform function instance with an apply method that calls toString on any enum value if not null, or returns null if the value is null.

Unfortunately, the compiler complains about the generic reference E extends Enum. I know that this expression can be used in non-anonymous contexts, e.g:

public static <E extends Enum<E>> String enumToString(E enumValue) {
    String result = null;
    if (enumValue != null) {
        result = enumValue.toString();
    }
    return result;      
}

The first, anonymous class definition is not OK. The second non-anonymous method definition is OK.

Is there anyway to define an anonymous class using Enums?

Thanks

Steven Solomon
  • 301
  • 1
  • 3
  • 14

3 Answers3

4

As Sotirios points out, generic type parameters can only be declared for classes or methods, not fields. In fact, declaring E is probably unnecessary - a Function<Enum<?>, String> would be fine:

Function<Enum<?>, String> ENUM_TO_STRING = new Function<Enum<?>, String>() {
    @Override
    public String apply(Enum<?> enumValue) {
        if (enumValue != null) {
            return enumValue.toString();
        }
        return null;
    }
};

Following that line of thought, toString is declared by Object, not Enum, so a general Function<Object, String> makes more sense:

Function<Object, String> NULLABLE_TO_STRING = new Function<Object, String>() {
    @Override
    public String apply(Object obj) {
        if (obj != null) {
            return obj.toString();
        }
        return null;
    }
};

In general, it's better to hide implementation details behind factory methods, making future changes easier, for example:

private static final Function<Object, String> NULLABLE_TO_STRING = ...

public static Function<Object, String> nullableToString() {
    return NULLABLE_TO_STRING;
}

Notice that a Function<Object, String> is still being returned. This should be fine as long as your APIs make use of PECS (e.g. accepting Function<? super Foo, ? extends Bar>), but we can also go a step further to offer a specific input type by making the method generic:

private static final Function<Object, String> NULLABLE_TO_STRING = ...

public static <T> Function<T, String> nullableToString() {
    @SuppressWarnings("unchecked") // safe contravariant cast
    final Function<T, String> withNarrowedType =
            (Function<T, String>)(Function<?, String>)NULLABLE_TO_STRING;
    return withNarrowedType;
}

Notice that this uses the same Function instance, with a unchecked cast to narrow its input type. The cast is safe because NULLABLE_TO_STRING accepts any object and is stateless. This pattern is demonstrated in Joshua Bloch's Effective Java item 27, "favor generic methods".

Community
  • 1
  • 1
Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
2

You can't just use generic type variables anywhere. You have to be in a context where they can be declared and are accessible.

In your second example, they are declared in the method signature.

In your first example, it doesn't seem like E is declared anywhere. In other words, you cannot declare a type variable as part of a variable declaration. You could do what you are trying if the method or class that piece of code appeared in declared the type variable E itself.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
0

You probably don't need generics at all here since you don't actually use the type information and it is lost at runtime anyway. Start first with the old-school Function apply (Enum enumValue). If you really don't want to suppress the warnings and need the type checking, then you can investigate further from there.

Eric Woodruff
  • 6,380
  • 3
  • 36
  • 33