22

I have a servlet with several methods that get a list of objects from the DAO, turn the list into JSON, and send it back in the response. Every list is made of objects that have a method:

public String getAsJson(){...}

And the servlet has a bunch of mostly identical methods that look like:

private String getUserListAsJson() {
    List<User> userList = this.dao.getUsers();
    StringBuilder builder = new StringBuilder();
    builder.append('[');
    // loops over the list appending the value of each objects getAsJson()
    builder.append(']');
    return builder.toString();
}

The problem is that I have about 6 methods (and growing) that look exactly like that except for different DAO queries. My idea was to create an interface that only had the definition for the getAsJson() method, make each bean implement that, and then have another method in the servlet that took objects that implemented that interface. Ended up looking like this:

public Interface JsonEnabled {
    public String getAsJson();
}

public class User implements JsonEnabled {
    ....
    @Override
    public String getAsJson() {...}
}

public class TheServlet {
    ...
    private String getUserListAsJson() {
        List<User> userList = this.dao.getUsers();
        return this.getListAsJson(userList);
    }
    private String getListAsJson(List<? implements JsonEnabled> list) {
        // The loop code that is in each method.
    }
}

That doesn't compile though. After looking up some documentation from Oracle, you can only have extends and not implements for generic parameters. Making all the classes extend from an Abstract Class that just has the getAsJson() method doesn't make sense semantically (the classes are unrelated).

I haven't found a good solution on SO or just googling around, so any help/insight would be appreciated.

Windle
  • 1,385
  • 2
  • 14
  • 33
  • 1
    tangental but do you really want to build JSON strings yourself? Suggest using a library like Jackson or something else – matt b Apr 10 '12 at 13:58

2 Answers2

50

For generic wildcards the keyword extends works for both classes and interfaces:

private String getListAsJson(List<? extends JsonEnabled> list) { ... }

extends has slightly different meaning when used for defining generic bounds - it essentially translates to "is, or extends, or implements".

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • Thank you! This worked like a charm. I figured that there had to be a way to do this - guess I should go read the documentation again now that you've explained it and see if the docs say the same thing. – Windle Apr 10 '12 at 14:13
  • I wish i could give this many many +1s This is exactly what I needed too, and now I understand what it means as well. – reidisaki Sep 18 '17 at 18:01
2

Why don't just use

private String getListAsJson(List<JsonEnabled> list) { ... }

?

Vadym S. Khondar
  • 1,428
  • 2
  • 12
  • 19
  • 3
    Based in OP code sample, you coudln't pass a `List` parameter to that function. – Luiggi Mendoza Apr 10 '12 at 14:06
  • 1
    @LuiggiMendoza nailed it. Feels like if I am going to go through the trouble of casting each list to a list I might as well just do the JSON there. I had the same thought though. – Windle Apr 10 '12 at 14:10
  • @LuiggiMendoza I see, thanks. Seems like I've missed this point. However you still can create List and fill it with Users and then pass it too such method. – Vadym S. Khondar Apr 10 '12 at 14:24
  • 2
    @VadymS.Khondar that's an option, but suppose you have your `UserBuilder` class that returns a `List`. Will you need to write a function to convert `List` to `List` and use it everytime to call `getListAsJson` function, what if you have more classes that implements JsonEnabled, that would mean more methods to convert those lists? – Luiggi Mendoza Apr 10 '12 at 14:30