1

I have an enum which is implements an interface. Enum:

enum MyEnum implements MyInterface {
    ONE, TWO, THREE;

    @Override
    public MyEnum getFirst() {
        return ONE;
    }
}

Interface:

interface MyInterface<T extends Enum<T>> {
    T getFirst();
}

Also I have a generic class with bounds:

class MyClass <T extends Enum<T> & MyInterface> {
   private T firstElement;
   private Class<T> enumType;

   MyClass (Class<T> enumType) {
      this.enumType = enumType;
   }
}

So the main idea is to pass any enum (which is implements MyIterface) into constructor and then work with its constants. And this works for me. But also I want to store this first element into firstElement private field. I tried something like this:

firstElement = ((MyInterface)enumType).getFirst();

But still no success. I can't cast java.lang.Class<T> to MyInterface. Any ideas how to achieve this? Thanks in advance!

UPDATE: My problem is not about how to take the first enum constant. I know about .ordinal() and .values()[0];. I want to create reusable generic class and use it with any enums, marked by some interface. Ok, let it not be getFirst() method. Let it be getDefault()

Yevhen Danchenko
  • 1,039
  • 2
  • 9
  • 17
  • 2
    @ΦXocę웃Пepeúpaツ all enums extend that class, it does make sense :) – niceman May 23 '17 at 14:59
  • Possible duplicate of [How do I invoke a Java method when given the method name as a string?](https://stackoverflow.com/questions/160970/how-do-i-invoke-a-java-method-when-given-the-method-name-as-a-string) – niceman May 23 '17 at 14:59
  • 1
    enumType is of Class type, it will not have your enum method. Only MyEnum.ONE, MyEnum.TWO so forth will have that method available. – Sikorski May 23 '17 at 14:59
  • also see this : https://stackoverflow.com/questions/11594797/dynamically-load-a-class-and-invoke-a-method-in-java – niceman May 23 '17 at 14:59
  • 1
    @niceman I don't think he needs reflection API here, he is trying to make it generic in a clean way. – Sikorski May 23 '17 at 15:00
  • 1
    @Sikorski hmm OP is missing the T in `& MyInterface`, when that is included I guess `firstelement.getFirst()` will work – niceman May 23 '17 at 15:03
  • by the way what do `MyInterface` and `MyClass` represent ? is it just for curiosity sake ? – niceman May 23 '17 at 15:06
  • @niceman, I just want to create a generic class to work with different enums, marked by some interface – Yevhen Danchenko May 23 '17 at 15:37
  • 2
    Don't use raw types! Give a try of your code but without the raw types. – Lew Bloch May 23 '17 at 17:49
  • and what would that generic class rerpesent ? like is it a `Person` ? a `ModuleManager` ? a Utility class maybe ? clearly we have [an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) here – niceman May 23 '17 at 18:06
  • Do you need to get the class of the enum as well? Or is it enough to access `MyInterface` methods? – fps May 24 '17 at 01:35

4 Answers4

1

From your context I think you need getFirst() because you want to get the first value of a enum. However, you don't need getFirst() at all. All you need is this:

class <T extends Enum<T> & MyInterface> MyClass {
   private T firstElement;
   private Class<T> enumType;

   MyClass (Class<T> enumType) {
      this.enumType = enumType;
      firstElement = enumType.getEnumConstants()[0];
   }
}

You can just remove all those getFirst() in all of your classes.

glee8e
  • 6,180
  • 4
  • 31
  • 51
  • @niceman Uh, I'm afraid the OP may cry that `MyInterface` is a marker interface and can't be removed. Fo example [CopyOption](http://docs.oracle.com/javase/8/docs/api/java/nio/file/CopyOption.html). – glee8e May 23 '17 at 15:06
1

You can call getFirst only on an enum object which implements MyInterface but not on its class. You can write:

firstElement = enumType.getEnumConstants()[0].getFirst();
wero
  • 32,544
  • 3
  • 59
  • 84
  • 1
    but since enums are ordered I don't understand why you want a `getFirst()` method at all. – wero May 23 '17 at 15:06
  • Thanks, but I know about `.values()[0]`, `.ordinal()` and `.getEnumConstants()`. My question is not about how to get the first enum constant. It's about generics. I'll update my post. – Yevhen Danchenko May 23 '17 at 15:34
  • @YevhenDanchenko check my answer: `enumType.getEnumConstants()[0]` is only used to get from `Class` to a `T` object and then invoke `getFirst()` (or `getDefault()`) on this object. – wero May 23 '17 at 15:43
  • I get error with this solution: `java.lang.Enum cannot be converted to T` – Yevhen Danchenko May 23 '17 at 15:51
  • @YevhenDanchenko because you did not add `` to `MyInterface` in the declaration of `MyClass`: it should read `class MyClass & MyInterface>` – wero May 23 '17 at 15:53
1

I think you could use Supplier<T> as an argument for your constructor instead of Class<T>:

class MyClass<T extends Enum<T> & MyInterface<T>>  {
    private T firstElement;
    private Class<T> enumType;

    @SuppressWarnings("unchecked")
    MyClass (Supplier<T> enumSupplier) {
        T actualEnum = enumSupplier.get();
        this.enumType = (Class<T>) actualEnum.getClass();
        this.firstElement = actualEnum.getFirst();
    }
}

That cast throws a warning, but it can be safely suppressed, since Supplier<T> will return an instance of T, and T is both Enum<T> and MyInterface<T>, so the class of actualEnum will always be Class<T>.

I would like to suggest you a few corrections: as MyInterface is generic, you should always use it along with a generic type (this is why I've declared MyClass as MyClass<T extends Enum<T> & MyInterface<T>>). The same goes for MyEnum: it should be defined as MyEnum implements MyInterface<MyEnum>.

With the above changes in place, you can now create an instance of MyClass as follows:

MyClass<MyEnum> myClass = new MyClass<>(() -> MyEnum.THREE); // Or ONE or TWO

System.out.println(myClass.firstElement); // ONE
fps
  • 33,623
  • 8
  • 55
  • 110
0
interface MyInterface {
    MyInterface getFirst();

    MyInterface[] getConstants();
}

enum MyEnum implements MyInterface {
    ONE, TWO, THREE;

    @Override
    public MyInterface getFirst() {
        return ONE;
    }

    @Override
    public MyInterface[] getConstants() {
        return new MyInterface[] {ONE, TWO, THREE};
    }
}

class MyClass {
   private MyInterface firstElement;

   MyClass (MyInterface enumType) {
      this.firstElement = enumType.getFirst();
   }
}

So, using the above design I think you can "pass any enum (which is implements MyInterface) into constructor and then work with its constants."

diogenesgg
  • 2,601
  • 2
  • 20
  • 29
  • 1
    My point is that, when you use some attribute like "enumType", you're not using Interfaces properly. Because, at some point you would have to write something like " if (enumType == SomeType) " or if (enumType SomeType), and those don't seem to be a good design. Interfaces are used mainly to replace this 'if else' logic (or switch) for type checking. – diogenesgg May 23 '17 at 15:47
  • Thanks. It seems it makes sense. Perhaps I have problems with architecture. – Yevhen Danchenko May 23 '17 at 18:59