How to Unpack/ Simplify Anonymous Inner Class

120 Views Asked by At

The goal of this post is to find out how to avoid using anonymous inner classes.

I have not worked extensively with inner anonymous classes but I am trying to simplify the following lines of code in someone's package examples.introduction.novice.simpler.model;

public class Person {
    public final String name;

// --- Attribute
    public static final Attribute<Person, String> NAME = new SimpleAttribute<Person, String>("name") {
        public String getValue(Person person, QueryOptions queryOptions) { return person.name; }
    };

}

So to unpack the above & make it easier to read I rewrote it so (please correct me if I am wrong):

public class Person {
    public final String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    
    // TODO: Simplify
    // ----------- Attributes -------------
    public static final Attribute<Person, String> NAME = new Name<Person, String>("name") ;

}

And ofcourse I created the Name class so :

public class Name<O, A> extends SimpleAttribute<O, A> {
    public Name(String attributeName) {
        super(attributeName);
        // TODO Auto-generated constructor stub
    }

    public String getValue(Person person, QueryOptions queryOptions) { return person.name; }

    @Override
    public A getValue(O arg0, QueryOptions arg1) {
        // TODO Auto-generated method stub
        return null;
    }


}

However, I am getting an error so :

Exception in thread "main" java.lang.ExceptionInInitializerError
    at examples.introduction.novice.simpler.model.Introduction.main(Introduction.java:17)
Caused by: java.lang.IllegalStateException: Attribute 'name' (class examples.introduction.novice.simpler.model.Name) is invalid, cannot read generic type information from it. Attributes should typically EITHER be declared in code with generic type information as a (possibly anonymous) subclass of one of the provided attribute types, OR you can use a constructor of the attribute which allows the types to be specified manually.
    at com.googlecode.cqengine.attribute.support.AbstractAttribute.readGenericObjectType(AbstractAttribute.java:139)
    at com.googlecode.cqengine.attribute.support.AbstractAttribute.<init>(AbstractAttribute.java:43)
    at com.googlecode.cqengine.attribute.SimpleAttribute.<init>(SimpleAttribute.java:55)
    at examples.introduction.novice.simpler.model.Name.<init>(Name.java:11)
    at examples.introduction.novice.simpler.model.Person.<clinit>(Person.java:23)
    ... 1 more

To recap: I am trying to simplify the following example in the following package (this is the original working example that I am trying to simplify and it has two classes: the main class called Introduction.java and the accompanying class called Person.java): https://github.com/mrarthurwhite/CQEngineIntroExample/tree/master/src/examples/introduction/novice

The attempt at simplifying this is in the following package (it just attempts to unpack the Person.java class : https://github.com/mrarthurwhite/CQEngineIntroExample/tree/master/src/examples/introduction/novice/simpler/model

As the kind & patient reader may observe from above , I am trying to unpack the anonymous inner class for Attribute . This is the link to the Attribute class & the cqengine library: https://github.com/npgall/cqengine/blob/master/code/src/main/java/com/googlecode/cqengine/attribute/Attribute.java

Also following is the link to the child class SimpleAttribute which is a descendant of Attribute: https://github.com/npgall/cqengine/blob/master/code/src/main/java/com/googlecode/cqengine/attribute/SimpleAttribute.java

2

There are 2 best solutions below

3
Alex On
public class Name extends SimpleAttribute<Person, String> {
    public Name() {
        super("name");
        // TODO Auto-generated constructor stub
    }

    @Override
    public String getValue(Person person, QueryOptions queryOptions) { 
        return person.name; 
    }
}

Try this. If you make a new generic implementation you have to basically use reflection to get the value. If you do that it will be way more complicated and not simplier.

5
Sweeper On

For your code, the Java compiler would generate a more specific, non-generic concrete class, like this (of course the name would be different):

public class Name extends SimpleAttribute<Person, String> {
    public Name(String attributeName) {
        super(attributeName);
    }

    @Override
    public String getValue(Person person, QueryOptions queryOptions) { return person.name; }

}

Notice how the superclass type is SimpleAttribute<Person, String>. The type arguments for SimpleAttribute are all concrete types, rather than type variables O and A like in your attempt.

And you'd use it like this:

public static final Attribute<Person, String> NAME = new Name("name");

Judging from the exception message,

Attribute 'name' (class examples.introduction.novice.simpler.model.Name) is invalid, cannot read generic type information from it.

the library that you are using relies on this behaviour (the fact that the type arguments are concrete types) to read the type arguments. It probably calls something to the effect of:

((ParameterizedType)NAME.getClass().getGenericSuperclass()).getActualTypeArguments()

If the type arguments for SimpleAttribute are O and A, like in your attempt at unpacking the anonymous class, the returned array will not have any real types, and that's possibly why it says "cannot read generic type information".

On the other hand, if you use an anonymous class (or the code from the beginning of the answer), then the generated inner class will have SimpleAttribute<Person, String> as the superclass, and the library would be able to read useful information from it.

Also note that, even if the library doesn't happen to read the generic type arguments, your unpacking of the anonymous class still doesn't do the same thing as the original code. Namely, the way you overrode the abstract method:

@Override
public A getValue(O arg0, QueryOptions arg1) {
    // TODO Auto-generated method stub
    return null;
}

is not the same as how the abstract method was overridden in the original code:

public String getValue(Person person, QueryOptions queryOptions) { return person.name; }

The other overload of getValue that you defined in Name isn't really relevant.