1

I have an API that returns JSON lists of entities, such as Gene, Protein, Resource, etc. For example, the Protein endpoint returns:

{
    next: "/api/1.0/protein?cursor=200",
    entities: [
    {
        symbol: "TARSH_HUMAN",
        href: "/api/1.0/protein/TARSH_HUMAN"
    },
    {
        symbol: "ABL1_HUMAN",
        href: "/api/1.0/protein/ABL1_HUMAN"
    },
    ...

The Resource endpoint is exactly the same, except symbol is name.

Solution so far

What I'd like to do is create some sort of EntityListSchema that can generate such lists no matter what the entity is. Here's what I have:

public class EntityListSchema<T extends JsonModel> {

    private String next;

    private List<T> entities;

    public EntityListSchema(Class<T> klass, int startAt) {
        int nextInt = startAt + Constant.API_MAX_RESULTS;
        this.next = "/" 
            + Constant.API_URL + "/"
            + klass.getEndpoint() + "?" // <-- This is the problem
            + Constant.API_CURSOR + "=" + nextInt;
    }

    ...

In principle, each Entity can then just extend JsonModel to ensure that getEndpoint is available, and we're done. But I'm seeing the error:

The method getEndpoint() is undefined for the type Class<T>

My understanding is that this error occurs because the method getEndpoint is not static. But if it were static (I'm using Java 8, so I can have static interfaces) I could not return the correct endpoint depending on the class.

Is it possible to achieve what I want? This would reduce a ton of duplicate code.

3
  • Class<T> klass doesn't represent an instance of the class, you need only change for: T klass Commented Sep 11, 2015 at 17:24
  • 2
    your question is actually, how do we associate metadata per class Commented Sep 11, 2015 at 17:45
  • @bayou.io, you may be right. I'd definitely listen to a redesign proposal if that's what you're suggesting, although it may not technically answer the question. Commented Sep 11, 2015 at 19:31

3 Answers 3

2

You are confusing an object instance and its class, for example for a string, String.class is the class (your Class<T>) and "foobar" is an instance (your T elements in the list), in this example, only "foobar" has String methods, String.class has Class methods.

My suggestion is to use some annotation here to define the "endpoint" then use Class#getAnnotation

Sign up to request clarification or add additional context in comments.

Comments

2

You had a confusion between a Java Class and a Instance of a class,

Class is the definition. An Instance of that class is the implementation.

Class<T> klass

represents the Class, this doesn't represent an object T or an instance of T, it represents the Class itself, with te variable names, the class name, the methods name, but no his implementations:

See: Class java

Instead of usin Class<T> you need to use only T

Learn more on java generics

Here is your code:

public EntityListSchema(T klass, int startAt) {
        int nextInt = startAt + Constant.API_MAX_RESULTS;
        this.next = "/" 
            + Constant.API_URL + "/"
            + klass.getEndpoint() + "?" // <-- This is the problem
            + Constant.API_CURSOR + "=" + nextInt;
}

Comments

1
public EntityListSchema(Class<T> klass, int startAt) {
...
    + klass.getEndpoint() 

So kclass can be Gene, Protein, etc. All Protein instances share the same endpoint, so you want to call klass.getEndpoint.

But: the erased type of Class<T> is Class, which does not have the method. You are really looking for a static virtual method which do not exist.

There are a few ways to solve this: registry pattern is one option; annotation on the class is another explained in another answer here.

But it may be simplest in your case to use an instance method itself, assuming you need this.next only when this.entities is not empty. If you delay assigning this.next then you can declare getEndpoint to be in instance method and you can take its value from the first element on this list. Alternatively you can initialize with the list itself:

class JsonModel {
    abstract String getEndpoint();
    ...
}
...
public EntityListSchema(List<T> entities, int startAt) {
    this.entities = entities;
    if (!entities.empty()) {
        int nextInt = startAt + Constant.API_MAX_RESULTS;
        this.next = "/" 
            + Constant.API_URL + "/"
            + entities.get(0).getEndpoint() + "?" // <-- This is the problem
            + Constant.API_CURSOR + "=" + nextInt;
}

PS BTW props for recognizing endpoint as a single word so naming it getEndpoint, not getEndPoint.

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.