4

My class is like this:

class X {}
class Y extends X {};
class Z extends X {};

I have an enum for each subclass (id + class):

enum Type {
   Y_TYPE(1, Y.class), Z_TYPE(2, Z.class);
   int id;
   Class c;
   public Type(int id, Class c) { this.id = id; this.c = c; }
   public static X createInstance() throws Exception {
      return c.newInstance();
   }
}

Then I used them as follows:

X parseFromID(int id) {
   for (Type v : Type.values()) {
     if (v.id == id) {
        return v.createInstance();
     }
   }
}

It works fine but I'm wondering if this a Java-ist way to create data based on integer id ? Is there any bad thing that should look for ?

Is there a way to enforce the class that is passed into are of X type without lengthy if-else condition? Think when I have a large number of subclasses.


Why do you want to work on integer ids?

I'm writing some sort of parser, so I need to convert integer id that I've taken from somewhere to the appropriate object.

3 Answers 3

7

There is really no reason to use reflection here. Throwing Exception is also a bad practice, and if you didn't use reflection, you wouldn't have to deal with reflection exceptions. You could simply do

enum Type {
    Y_TYPE(1) {
        @Override
        public X createInstance() {
            return new Y();
        }
    }, Z_TYPE(2) {
        @Override
        public X createInstance() {
            return new Z();
        }
    };

    private int id;

    private Type(int id) { 
        this.id = id; 
    }

    public abstract X createInstance();
}

This is also helpful because it doesn't force every subclass to have a public no-arg constructor, and also allows returning the same instance of X or Y, if possible.

If you're concerned about the verbosity of the anonymous class definitions, you could replace them with lambdas, if you're using Java 8:

import java.util.function.Supplier;

enum Type {
    Y_TYPE(1, X::new), Z_TYPE(2, Y::new);

    private int id;
    private Supplier<X> supplier;

    private Type(int id, Supplier<X> supplier) {
        this.id = id;
        this.supplier = supplier;
    }

    public X createInstance() {
        return supplier.get();
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

I think about this, but it is going to become a very long code very soon if I have a large number of subclasses right ?
It's a bit verbose, but not so long. See my edited answer for a less verbose solution.
The second version doesn't look very different from mine, what is the main advantage ?
The advantages are in the answer. Read it. No exceptions, type safety, flexibility in implementing the supplier, better performance
How can we suffice for object creation via parameterized contructor?
2

Using a factory and a map is more academic:

import java.util.HashMap;
import java.util.Map;

interface Factory<T> {

    T createInstance();
}

class X {/**/}
class Y extends X {/**/}
class Z extends X {/**/}

class Factories {

   static Map<Integer, Factory<?>> factories = new HashMap<>();
   static {
      factories.put( 1, X::new );
      factories.put( 2, Y::new );
      factories.put( 3, Z::new );
   }

   @SuppressWarnings("unchecked")
   static <T> Factory<T> get( int id ) {
      return (Factory<T>)factories.get( id );
   }
}

public class Main {

   static void main( String[] args ) {
      final Factory<X> fx = Factories.get( 1 );
      final X x = fx.createInstance();
      final Factory<Y> fy = Factories.get( 2 );
      final Y y = fy.createInstance();
      final Factory<Z> fz = Factories.get( 3 );
      final Z z = fz.createInstance();
   }
}

Comments

0

Is there a way to enforce the class that is passed into are of X type without lengthy if-else condition?

Yes, you can use generics to restrict the class. Change the constructor to:

public Type(int id, Class<? extends X> c) { this.id = id; this.c = c; }

Why do you want to work on integer ids? You can use either the enum values directly or - if you need to transfer or store them - their string representation and parse the String if needed using the enum's valueOf method.

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.