views:

366

answers:

5

I'm having some problems with a Wicket 1.3 -> Wicket 1.4 migration, but this question could be applied to Java generics overall, too. The migration has caused hundreds of warnings to spring up out of nowhere -- for those unfamiliar with Wicket, many Wicket classes are derived from a common ancestor, which became generified in v1.4 -- and I'm not sure what parameters to apply in some cases, mostly assorted forms and tables. I'm thinking they could do with <?>, <Object> or <Void>, but I'm not sure which.

<?> seems most appropriate to me, but there are many places where I can't use a wildcard. <Object> works in all cases, but it makes me uneasy because it's basically writing a wildcard without using the wildcard, which just feels inherently wrong to part of my brain. And using <Void> was suggested in the Wicket migration guide.

So what is the proper thing to do in this case?


EDIT 2: I think my first edit (now at the bottom of the question) confused people by making it seem like I was just asking about collections of strings. Here are other examples and their warnings:

public class DocumentProcessor extends Form implements DocumentManagement { ...

Form is a raw type. References to generic type Form should be parameterized

AjaxFallbackDefaultDataTable theTable = new AjaxFallbackDefaultDataTable("theTable", cols, dataProvider, recPerPg);

Multiple markers at this line
- Type safety: The constructor AjaxFallbackDefaultDataTable(String, List, ISortableDataProvider, int) belongs to the raw type AjaxFallbackDefaultDataTable. References to generic type AjaxFallbackDefaultDataTable should be parameterized
- AjaxFallbackDefaultDataTable is a raw type. References to generic type AjaxFallbackDefaultDataTable should be parameterized
- AjaxFallbackDefaultDataTable is a raw type. References to generic type AjaxFallbackDefaultDataTable should be parameterized


EDIT: I was hoping to make the question so broad it didn't need sample code, but here is some.

List<IColumn> columns = new ArrayList<IColumn>();
columns.add(new PropertyColumn(new Model<String>("Number"), "revisionID"));

These warnings are generated:

Multiple markers at [the first] line
- IColumn is a raw type. References to generic type IColumn should be parameterized
- IColumn is a raw type. References to generic type IColumn should be parameterized

Multiple markers at [the second] line
- Type safety: The constructor PropertyColumn(IModel, String) belongs to the raw type PropertyColumn. References to generic type PropertyColumn should be parameterized
- PropertyColumn is a raw type. References to generic type PropertyColumn should be parameterized

There are no errors.

A: 

The warnings say that IColumn interface and PropertyColumn class are parameterized types, so you just need to define type parameters for them.

Consider the following example:

List<Set> list = new ArrayList<Set>();

List and ArrayList are parameterized types and their type parameters are defined. However, Set is parameterized type as well but it's raw version is used as a type parameter, hence, compiler generates a warning at that case.

We can fix our example by explicitly specifying all type arguments, e.g.

List<Set<Integer>> list1 = new ArrayList<Set<Integer>>();
List<Set<String>> list1 = new ArrayList<Set<String>>();

You need to do the same thing for your generic classes and interfaces.

denis.zhdanov
I understand that I need parameters, but I'm not clear on which one to apply. Can you suggest between wildcard, Object and Void, or explain why none of those is correct?
Lord Torgamus
It depends on your application. I.e. your question looks like 'I have List<Set>. What type parameter should I use for Set?'. The answer is 'you should user "right" parameter. "Right" definition depends from particular situation.'
denis.zhdanov
A: 

I haven't used wicket and Vodafone is blocking me from seeing API docs. However, it appears that you are missing many generic arguments and want something like:

List<IColumn<String>> columns = new ArrayList<IColumn<String>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

If you want to add other IColumns with unrelated generic argument, you will need something like;

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
columns.add(new PropertyColumn<String>(new Model<String>("Number"), "revisionID"));

Or if you need to get at the column's properties, perhaps something like:

List<IColumn<String>> strColumns = new ArrayList<IColumn<String>>();
List<IColumn<?>> columns = new ArrayList<IColumn<?>>();
PropertyColumn<String> column =
    new PropertyColumn<String>(new Model<String>("Number"), "revisionID");
strColumns.add(column);
columns.add(column);
Tom Hawtin - tackline
A: 

Alternatives available would be:

  1. To just use the raw type, as you have in the sample code, simply ignore the warnings
  2. To use the wildcard/Object generic
  3. To use an extends generic

I am assuming from your question that #1 is not a viable option for you.

Example for #2 (wildcard/Object)

List<IColumn<?>> columns = new ArrayList<IColumn<?>>();

OR

List<IColumn<Object>> columns = new ArrayList<IColumn<Object>>();

IMO I don't think it really matters whether you choose ? or Object, and neither one is more correct than the other, at least functionally.
If you don't care what the generic is, and you never access it, then it is of nigh consequence; although think ahead carefully, if indeed it is possible you would use generics here in the future. This will likely be the case only where in your pre-migration code, you found yourself not having to typecast anything from within the IColumn objects.

Example for #3 (extends generic)

Create a supertype or common interface to all the possible generics of the IColumn type. Where
T extends MyType:

List<IColumn<T>> columns = new ArrayList<IColumn<T>>();

I would base the decision in choosing, between the 2nd and 3rd method, on what the possible generic attributes for IColumn actually are.

  • If they are your own classes AND you actualy want to access objects of the generic type, I would go for the 3rd method,
  • otherwise, for example with String or boxed primitives such as Integer, or if you don't acees the objects of the generic type, I would go for method 2.

HTH

bguiz
+1  A: 

Semantically, using <?> means "I don't know the type, and I actually don't care at all. Using anything else sets expectations on the form of the expected content. Practically, <Object> does the same, but states you'll use the properties of your generic that use the parameter type.

So the rule of thumb should be:

  • if you only work on the genetic object but not with it's parametrized content, use <?> so you know at first sight the parameter doesn't matter to the behavior.
  • in any other case, use the most specific parameter that encompasses all types your method is designed to work with. Extreme case is <Object>, other include <? extends SomeTopLevelType>
Romain
A: 

You want to use the type of the model associated with your component. That is, use the type returned by a call to getModelObject(). So, to use an example from the migration guide:

ListView<Person> peopleListView = new ListView<Person>("people", people) {
    protected void populateItem(ListItem<Person> item) {
        item.add(new Link<Person>("editPerson", item.getModel()){
            public void onClick() {
                Person p = getModelObject();
                setResponsePage(new EditPersonPage(p));
            }
        });
    }
};

With generics it is easy to tell that that is a list of Persons, with a link to an edit page that uses a person as it's model. Unfortunately, very often in wicket your components won't have a model associated with them. In that case getModel() will return null so the proper type to use is <Void>, which is essentially a place holder for null.

DocumentProcessor

public class DocumentProcessor extends Form implements DocumentManagement { ...

if you aren't setting the model for DocumentProcessor it would look like so:

public class DocumentProcessor extends Form<Void> implements DocumentManagement {
    public DocumentProcessor(String id) {
        super(id);
        ....

but with a model DocumentProcessor looks something like this:

public class DocumentProcessor extends Form<Document> implements DocumentManagement {
    public DocumentProcessor(String id, Document doc) {
        super(id, doc);

AjaxFallbackDefaultDataTable

Judging from it's constructors AjaxFallbackDefaultDataTable will likely store either an IColumn[] or List in it's model, but for your implementation you don't know or care so <?> is appropriate, the difference between this and DocumentProcessor is that you're extending Form and therefore do know and do care how it is using it's model.

IColumn

For the IColumn/PropertyColumn example, I'm going to assume that the revisionID field is a Long, then I would write it like so:

List<PropertyColumn> columns = new ArrayList<PropertyColumn>();
columns.add(new PropertyColumn<Long>(new Model<String>("Number"), "revisionID"));

You might look at

More 1.4 Migration info

Void type parameter

perilandmishap