0

I could workaround this problem but I cannot understand it, so I am asking for some explanation (and maybe a better question title as well).

Please consider this:

public class TBGService {
    // TBGObject is an abstract base class which is extended by model classes
    public <T> T doGet(TBGObject model) throws TBGServiceException {
        String uri = model.buildUrl(repository) + model.getObjectKey();
        GetMethod method = new GetMethod(uri); 
        T returned = execute(method, credentials, model.getClass());
        return returned;
    }
}

and this:

public enum TBGTaskAttributes {
    private TBGTaskAttributes(String id, String type, String label, Object... flags) {
        builder = new TaskAttributeBuilder();
        builder.withId(id).withLabel(label);
        for (Object flag : flags) {
            processFlag(flag);
        }
    }

    public abstract String getValueFromIssue(TBGIssue issue);

    public abstract void setValueInIssue(TBGIssue issue, String value);

}

when I write this code to define an enum item:

PROJECT(TaskAttribute.PRODUCT, TaskAttribute.TYPE_SINGLE_SELECT, "Project", new OptionProvider() {
    @Override
    public Set<Entry<String, String>> getOptions(TaskRepository repository) {
        try {
            List<TBGProject> list = TBGService.get(repository)
                .doGet(new TBGProjects()).getProjects();
            [...]
            return map.entrySet();
         } catch (TBGServiceException e) { [...] }
         return null;
    }
}) {
    @Override
    public String getValueFromIssue(TBGIssue issue) {
        return issue.getProjectKey();
    }
    @Override
    public void setValueInIssue(TBGIssue issue, String value) {
        issue.setProjectKey(value);
    }
},
[... other items ...]

I get compiler error (also eclipse auto-completion does not work):

The method getProjects() is undefined for the type Object

and if I hover the doGet method, eclipse show it as defined like:

 <Object> Object TBGService.doGet(TBGObject model)

Elsewhere, hovering shows the signature correctly as:

 <TBGProjects> TBGProjects TBGService.doGet(TBGObject model)

when called with parameter new TBGProjects().

Just changing:

List<TBGProject> list = TBGService.get(repository)
    .doGet(new TBGProjects()).getProjects();

with:

TBGProjects projects = TBGService.get(repository).doGet(new TBGProjects());
List<TBGProject> = projects.getProjects();

makes it work. But what's happening here? What am I missing?

guido
  • 18,864
  • 6
  • 70
  • 95
  • BTW, passing a class instance like that is very inefficient. You should accept a `Class` parameter and pass `TBGProjects.class` instead. – SLaks Jan 21 '13 at 03:39
  • I see; but actually the passed class contains logic required in abstract superclass. The entry point knows the input for calling the contructor, the service (being a json client) knows nothing about it except the abstract behaviour. Will try to find a better pattern for this design. – guido Jan 21 '13 at 04:10
  • You may want to use an annotation on the class instead of `getObjectKey()`. – SLaks Jan 21 '13 at 14:21

1 Answers1

3

Java infers the type of T based on what you assign the return value of the method to.

If you don't assign the return value to anything, Java has no idea what T should be.

To fix this, you can change the parameter to be of type T so Java can infer T from the parameter you pass:

public <T extends TBGObject> T doGet(T model) throws TBGServiceException {
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • oh yeah it was that easy, thanks! and congrats for your quickness in reading and understading someone else code – guido Jan 21 '13 at 00:03
  • side question: why did the same unchanged call work from another class? – guido Jan 21 '13 at 00:16
  • @guido: I don't know why that would work. Can you show me a complete example in which it compiles? – SLaks Jan 21 '13 at 00:19
  • sorry my bad; other class is calling a sibling method, in which the parameter is already generic. thanks again – guido Jan 21 '13 at 00:24
  • how do you know the OP intends for the parameter and the return to be the same type? – newacct Jan 21 '13 at 03:22
  • @newacct: Because he passes `model.getClass()` to `execute()`, and never uses the parameter elsewhere. – SLaks Jan 21 '13 at 03:39