Paul Taylor
Paul Taylor

Reputation: 13190

Better alternative to reflection than large switch statement using java 8

My old code create a FrameBody subclass based on identifier

Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody> Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + identifier);
Class<?>[] constructorParameterTypes = {Class.forName("java.nio.ByteBuffer"), Integer.TYPE};
Object[] constructorParameterValues = {byteBuffer, frameSize};
Constructor<AbstractID3v2FrameBody> construct = c.getConstructor(constructorParameterTypes);
frameBody = (construct.newInstance(constructorParameterValues));

But profile analysis shows its a bit slow, the alternative to using reflecting is a big switch statement

switch(identifier)
{
    case ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION:

        frameBody = new FrameBodyAENC(byteBuffer, frameSize);
        break;

    case ID3v24Frames.FRAME_ID_ATTACHED_PICTURE:

        frameBody = new FrameBodyAPIC(byteBuffer, frameSize);
        break;

                .......
    }   

but I have over 100 identifiers, so its seems a bit cumbersome

Is there a more elegant way to do this in Java 8 ?

Upvotes: 1

Views: 768

Answers (1)

Eugene
Eugene

Reputation: 120848

EDIT:

This can be simplified indeed as Holger suggests:

 Map<String, BiFunction<ByteBuffer, Integer, AbstractID3v2FrameBody>> LOOKUP = Map.of(
           FRAME_ID_AUDIO_ENCRYPTION, FrameBodyAENC::new,   
           FRAME_ID_ATTACHED_PICTURE, FrameBodyAPIC::new
 );

And then as simple as:

LOOKUP.get(ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION)
      .apply(byteBuffer, frameSize);

PREVIOUS SUGGESTION

This is just a sample, you will need to adapt it to your classes:

private static final Lookup L = MethodHandles.lookup();
private static final MethodHandle CONS;

static {
    try {
        CONS = L.findConstructor(SomeClass.class, MethodType.methodType(void.class, int.class));
    } catch (Throwable t) {
        throw new RuntimeException(t);
    }
}

private static final Map<String, MethodHandle> LOOK_UP = Map.of(
    "SOME_CLASS", CONS
);

public static void main(String[] args) {

    try {
        SomeClass sc = (SomeClass) LOOK_UP.get("SOME_CLASS").invokeExact(42);
        System.out.println(sc.getX());
    } catch (Throwable t) {
        t.printStackTrace();
    }

}

static class SomeClass {

    private final int x;

    public SomeClass(int x) {
        this.x = x;
    }

    public int getX() {
        return x;
    }
}

Upvotes: 2

Related Questions