Java - Can you access an Enum declared in Subclass from Super class? - java

I was hoping to declare in Enum type in a subclass and then access it from the super class. This is what I came up with, but it does not work:
class Subclass {
enum Pets {
CAT,
DOG;
}
Class<Pets> getEnumClass() {
return Pets.class;
}
}
class Superclass {
// This generates a warning:
abstract Class<? extends Enum> getEnumClass();
void PrintEnumNames() throws InstantiationException, IllegalAccessException {
Class<? extends Enum> enumClass = getEnumClass();
Enum newEnum = enumClass.newInstance();
for( Enum iEnum : newEnum.values()) { // newEnum.values() isn't available
System.out.printf("%s", iEnum.toString());
}
}
}

values() is a static method, you can't call it on the instance. To get enum values from the class, use Class.getEnumConstants():
Class<? extends Enum> enumClass = getEnumClass();
for (Object o: enumClass.getEnumConstants())
System.out.println(o);

Actually, you can call static methods on instances, but it is not possible to get an instance of an Enum this way. That is, this line won't work, and will throw an InstantiationException every time.
Enum newEnum = enumClass.newInstance();
This is because Enums are restricted in the values that they can have, and these values are set by the JVM when the class is initialised (Pets.CAT and Pets.DOG, in your example).

Related

Get list of Enums from generic class

I have a class that is paramaterised with an extend of Enum.
public class MyClass<EnumType extends Enum> {
public MyClass(){
Enum<?>[] enums = EnumType.getEnumConstants();
}
}
The line:
Enum<?>[] enums = EnumType.getEnumConstants()
fails to compile with "can not resolve method".
How can I get to the base type and get the enums?
OTOH, if I do the following it works ok:
public void setEnumType(Class <? extends Enum> clazz){
Enum<?>[] enums = clazz.getEnumConstants();
}
I can't pass this into the constructor as it is a custom view which is directly inserted in the parent.
Owing to erasure, you have to pass an instance of the enum class to the constructor:
public class MyClass<EnumType extends Enum<EnumType>> {
// ^ don't forget this
public MyClass(Class<EnumType> c){
Enum<?>[] enums = c.getEnumConstants();
}
}
MyClass<YourEnum> m = new MyClass<>(YourEnum.class);
Or, you could pass YourEnum.values() directly. The risk there is that a caller can pass any array, not necessarily one with all values, without duplicates, in the right order etc.

How to cast generic Class type into an another generic Class type in Java

This is what I want:
interface A {}
abstract class B implements A {}
class C extends B { /*few fields*/ }
class D extends B { /*few different fields*/ }
public void doSomething(Class<A> clazz) {
if(clazz.isAssignableFrom(B)) {
doSomethingElse((Class<? extends B>)clazz); // casting is good, but cannot pass this way to doSomethingElse
}
}
public <T extends B> void doSomethingElse(Class<T> clazz) {
if(clazz.isAssignableFrom(C)) {
// do something with C fields
} else if(clazz.isAssignableFrom(D)) {
// do something with D fields
}
}
But obviously I cannot cast the class type this way.
Is there a way to do it? I am very curious...
Thanks!
First of all, a value of type Class<A> can only be the class object for the exact class A, it cannot be any other class. The class object for some other class B would be Class<B>, and Class<B> is never a subtype of Class<A>. You probably want a Class<? extends A> parameter instead.
Second, your use of .isAssignableFrom() seems backwards (besides the fact that it's syntactically invalid as B is not a valid expression). clazz.isAssignableFrom(B.class) means that the class represented by clazz is a superclass of B, in which case it makes no sense to cast clazz to Class<? extends B>. You probably want B.class.isAssignableFrom(clazz) instead.
Once you make the first change above (make clazz type Class<? extends A>), the cast will compile, although it will be an unchecked cast. If you don't want an unchecked cast, instead of (Class<? extends B>)clazz, you can do clazz.asSubclass(B.class).

Get class of EnumSet's Enum

Is it possible to get the class of the Enum from a variable of type EnumSet.
Consider the following code:
enum Foo
{
FOO_0,
FOO_1,
}
<E extends Enum<E>> void fooBar(EnumSet<E> enumSet, Class<E> type)
{
EnumSet<E> none = EnumSet.noneOf(type);
// ...
}
void bar()
{
EnumSet<Foo> enumSet = EnumSet.of(Foo.FOO_1);
fooBar(enumSet, Foo.class);
}
Writing Foo.class in fooBar() seems redundant. I would like to extract the class from the enumSet inside fooBar() function. Is that even possible?
What I wish to do is just call fooBar(enumSet); and still be able to instantiate the none variable as EnumSet.noneOf().
Works for empty EnumSets also, and returns the correct enum type even when the element has a class body:
public static <T extends Enum<T>> Class<T> getElementType(EnumSet<T> enumSet) {
if (enumSet.isEmpty())
enumSet = EnumSet.complementOf(enumSet);
return enumSet.iterator().next().getDeclaringClass();
}

Simplest way to cast a Class<T> to a Class<E extends Enum<E>> without losing type information

I have a method createFoo() that creates instances of Foo<T> using the Class<T> instance for that T. Now I want to extend that method to forward calls that are made using an enum type to the method createEnumFoo(). Calling the second method from the first one seems to be non-trivial. Below is an example of how I managed to do it using two unchecked casts and an extra method, all of which I would like to get rid of.
The method castEnumType() is required because I couldn't find a way to cast a Class<?> to a Class<E extends Enum<E>> without having the E bound somewhere. This involves an unchecked cast because I have not found a way to do it using Class.asSubclass(). After creating the instance of Foo, I need to cast it from Foo<E> to Foo<T> event though E and T will always be the same types.
I can't weaken the signature of createEnumFoo() because it is calling Enum.valueOf(enumType, ...) and requires the result of this to be of type E.
final class Example {
<E extends Enum<E>> Foo<E> createEnumFoo(Class<E> enumType) {
// This makes use of e.g. Enum.valueOf(enumType, ...).
return null;
}
<E extends Enum<E>> Class<E> castEnumType(Class<?> enumType) {
return (Class<E>) enumType;
}
<T> Foo<T> createFoo(Class<T> type) {
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(castEnumType(type));
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T> {
}
}
Is there a simpler way to do this?
Some context
To clarify the problem I'm facing, I'll explain how this problem actually arose in a project I'm working on:
In the code where I came across this problem, Foo<T> is actually Converter<T>, which is an interface which allows an instance of T to be serialized and de-serialized from and to a JSON value:
public interface Converter<T> {
JsonObject encode(T value);
T decode(JsonObject data);
}
And createFoo() is actually a method converterForType() which takes a Class<T> instance and dynamically dispatches to a bunch of static methods and fields that create/contain converters for common Java types and types specific to the project. Normally when a converter is needed, the appropriate method/field is accessed directly but there are some places where the type is only known at runtime, which is where converterForType() is used.
Now I wanted to extend that method to automatically handle enum types by converting those to JSON strings containing the name of the enum constant. This is why I need to call the method enumConverter() from converterForType(). This is the implementation of enumConverter():
public static <E extends Enum<E>> Converter<E> enumConverter(final Class<E> enumClass) {
return new Converter<E>() {
public JsonObject encode(E value) {
return Json.convert(value.name());
}
public E decode(JsonObject data) {
return Enum.valueOf(enumClass, data.asString());
}
};
}
What about this, use raw types for createEnumFoo method
Edit: fixed compile error reported by #Feuermurmel in comments
#SuppressWarnings({ "unchecked", "rawtypes" })
final class Example
{
<E extends Enum<E>> Foo<E> createEnumFoo(Class enumType)
{
// This makes use of e.g. Enum.valueOf(enumType, ...).
Enum x = Enum.valueOf(enumType, "x");
return (Foo<E>) x;
}
<T extends Enum> Foo<T> createFoo(Class<T> type)
{
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(type);
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T>
{
}
}

Cast to super class type a "Class"

I have an interface
interface Inter extends Blah {
public void someMethod();
}
class Dummy {
Class<Blah> interfaceType;
public setInterfaceType( Class<Blah> input ) {
this.interfaceType = input;
}
}
class tester {
public void init() {
Dummy dummyObj = new DummyObj();
dummyObj.setInterfaceType( Inter.class ); //This complains that the type is not suitable
}
}
Compilation error:
The method setInterfaceType(Class) in the type Dummy is not applicable for the arguments (Class)
I tried casting input to Class<Blah> while calling setter but that isnt allowed either. Im not understanding why it doesnt accept a class of sub-class-type. Can anyone tell me whats happening here and how the setter can be invoked. The Dummy class is external so i cannot change it.
Generics are not covariant so you can't set to Class<Blah> object of type Class<Inter>. Think about it. If you would be able to use List<Fruit> list = new ArrayList<Apple>() then via list you would be able to add not only Apples but also other Fruits. Would that be OK?
To solve this problem try changing Class<Blah> to Class<? extends Blah>
You can also change your Dummy class to use generic type T
class class Dummy<T extends Blah> {
Class<T> interfaceType;
public void setInterfaceType(Class<T> input) {
this.interfaceType = input;
}
}
and use it like
Dummy<Inter> dummyObj = new Dummy();
dummyObj.setInterfaceType(Inter.class);
You most certainly means
Class<? extends Blah> interfaceType;
public setInterfaceType( Class<? extends Blah> input ) {
this.interfaceType = input;
}
Update
If the Dummy class can't be modified, then it is pretty dubius WHY there is such a setter method. But anyway, you may avoid the "generic type" check, by casting your argument to Class (without type argument), as in: dummyObj.setInterfaceType( (Class) Inter.class )

Categories