Cyril N.
Cyril N.

Reputation: 39889

Java, Generics and Classe<Generic> problems

Here's my Model's parent :

abstract public class ApiModel {
    // Problem 1
    public static ExpressionList<Model> find() {
        return null;
    }
    
    public static <T extends Model> T findById(Long id) {
        return null;
    }
}

A Model :

public class MyModel extends ApiModel {

    private static Model.Finder<Long,MyModel> find = new Model.Finder(Long.class, MyModel.class);

    public static ExpressionList<MyModel> find() {
        return find.where();
    }
    
    public static MyModel findById(Long id) {
        return find.byId(id);
    }
}

The parent's Controller :

public class ApiController<T extends ApiModel> extends Controller {
    public Result list() {
            // Problem 2
        ExpressionList<T> list = T.find();
        return ok(Json.toJson(list.orderBy("name"), 10));
    }
    
    public Result create() {
        return update(null);
    }
    
    public Result details(Long id) {
            // Problem 3
        T model = T.findById(id);
        
        // ...
        return ok(result);
    }
    
    public Result update(Long id) {
            // Problem 4
        Form<T> form = form(T.class).bindFromRequest();

            T model = form.get();

            // Problem 5
            T.save();

        // ...
        return ok(result);
    }
    
    public Result delete(Long id) {
        // ...
        return ok(result);
    }
}

A Controller

public class AController extends ApiController<MyModel> {
    public final static AController rest = new AController();
    
    private AController() {}
}

The problems I face :

  1. I would need find() to returns ExpressionList<T extends Model>, but if I put this, I have an error.
  2. Pb1 make this errors appears, it says "Type mismatch: cannot convert from ExpressionList to ExpressionList". I suppose by fixing 1., 2. will be fixed also.
  3. This one is odd, it returns "Bound mismatch: The generic method findById(Long) of type ApiModel is not applicable for the arguments (Long). The inferred type T&Model is not a valid substitute for the bounded parameter "
  4. Of course I can't use .class of this one. But how can I do then ?
  5. Since the model use annotation to have @Entity, I can't use it here, it's not recognized :/

I think everything is related. Maybe I poorly designed my code ?

Here's the reason of that kind of structure. I'm using PlayFramework (that have static controllers) and I like to do inheritance and thus generic models. But for that, I need instance and not static reference, thus the public final static AController rest. But then, I can't access static context of the Model (find & findById). So I made ApiModel. But it doesn't help more either.

Upvotes: 2

Views: 477

Answers (3)

Sornii
Sornii

Reputation: 421

From my point of view, it should be a bad idea to do that. You can use normal methods in play, you just need to reference the method in the route list with a @ in the beginning.

So, if you have a controller this way:

public class MyController extends Controller {
    public Result index() {
        return TODO;
    }
}

You can use like this in your routers:

GET         /           @controllers.MyController.index()

Upvotes: 0

Benoit
Benoit

Reputation: 1993

You do not need the ApiModel class if your rewrite a little ApiController:

public class ApiController<T extends Model> extends Controller {

    private Model.Finder<Long,T> finder;

    private final Class<T> modelClass;

    public ApiController(Class<T> modelClass) {
        this.modelClass = modelClass;
        finder = new Model.Finder(Long.class, modelClass);
    }

    public Result list() {
        ExpressionList<T> list = finder.where();
        return ok(Json.toJson(list.orderBy("name"), 10));
    }

    public Result details(Long id) {
        T model = finder.findById(id);

        // ...
        return ok(result);
    }

    public Result update(Long id) {
        // Problem 1
        Form<T> form = form(modelClass).bindFromRequest();

            T model = form.get();

            T.save();

        // ...
        return ok(result);
    }

    ...
}

public class AController extends ApiController<MyModel> {
    public final static AController rest = new AController();

    private AController() {
        super(MyModel.class);
    }

}

Upvotes: 2

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147164

I don't know why you need those static methods in ApiModel.

You should be able to endow ApiController with the Class for T.

public class ApiController<T extends ApiModel> extends Controller {
    private final Class<T> tClass;
    ApiController(Class<T> tClass) {
        this.tClass = tClass;
    }

public class AController extends ApiController<MyModel> {
    ....
    private AController() {
        super(MyModel.class);
    }

Upvotes: 0

Related Questions