1

In Java, is it possible to specify a generic method that matches lists containing any objects except enums?

public class Foo {
    public enum MyEnum {
        ENUM_VALUE
    }

    public static <T ...generics magic...> void bar(List<T> argument) {
        // .. code
    }

    public static void test() {
        Foo.bar(Arrays.asList(new Object()));      // OK
        Foo.bar(Arrays.asList(new Exception()));   // Still OK
        Foo.bar(Arrays.asList(MyEnum.ENUM_VALUE)); // Compiler error, please?
    }
}
  1. I'm under the impression that Java generics does not support <T does not extend Something>-style syntax, but I'd like to be know for sure - and preferably be proven wrong :).
  2. If I can't implement this limitation with generics, what is the best possible workaround? My current plan is below; is a better implementation possible?
public static <T> void bar(List<T> argument, Class<T> argumentType) {
  if (Enum.class.isAssignableFrom(argumentType)) {
    throw new IllegalArgumentException("Enums are disallowed.");
  }
}

Edit: I'm interested in this because I have an API that allows the consumer to:

  • Pass in a List of T and specify whether to return references to the T's in the list or clones of the T's:

public <T> void setList(List<T> contents, boolean returnClones)

  • Retrieve the T's from the list:

public <T> T get(int index)

Of course, if T is an Enum, then specifying returnClones with true doesn't make sense - we can't create a clone of the Enum. There are also other cases where cloning doesn't work (e.g. a no-args constructor is not available). I'm trying to make the API as robust as possible.

Edit #2: I know Enums are immutable; I'm looking for the best way of preventing the caller from accidentally specifying invalid arguments to the API. The caller should never

  • Pass in a List<SomeEnumType> and specifying that clones should be returned. This doesn't make sense.
  • Pass in a List<SomeObjectWithoutNoArgsConstructor> and specifying that clones should be returned. This makes sense but is something we cannot support (it might be possible to clone the objects, but without a no-args constructor we don't know how).

Cloneable is a good suggestion, but the purpose of the interface is different: "A class implements the Cloneable interface to indicate to the Object.clone() method that it is legal for that method to make a field-for-field copy of instances of that class." (Cloneable JavaDoc). Since we're using Dozer for our cloning instead of calling clone() on the source object, I think Cloneable would unfortunately end up hurting more than helping in this case.

3
  • Your work-around seems reasonable. Why do you want to do this though? Commented Feb 10, 2014 at 7:54
  • You could force your argument to implement Cloneable Commented Feb 10, 2014 at 8:24
  • Why do you need cloneing? Usually, cloning is used to return a new object to prevent the user to modify the original one. Since Enum is unmodifiable like int,long,String, it's still fit the purpose. It seems ok to permit enum here. Just my opinion. Maybe you have other concern here. Commented Feb 10, 2014 at 8:27

1 Answer 1

1

I did once a refactoring, where a long parameter changed to an Object. The solution was to do something like:

/**
 * Not for enums.
 * @param x use not an enum.
 */
@Deprecated
public <E extends Enum<E>> void f(E x) {
    throw new IllegalArgumentException("...");
    // Or better throw new UnsupportedOperationException("...");
}

public <T> void f(T x) {
    ...
}

With IDE support this will do nicely.


Best to mention in javadoc and exception message what to use instead, the other f or so.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.