Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

The

...

trouble

...

with

...

Generics

...

Excerpt

...

hidden

...

true

...

Hidden

...

Excerpt

...

Panel
borderStylesolid
titleTable of contents
Table of Contents
minLevel2
Info

You are kindly requested to modify/tweak and work on this document. Just one thing: KEEP IT CIVIL!

This document tries to capture the benefits and doubts we have regarding generics in Wicket 1.4. Ultimately we need to find a way where the Framework and Wicket Community can move onward to Wicket 1.5/2.0

...

with

...

Java

...

5

...

as

...

a

...

basis.

...

Generics

...

Rock!

...

Java

...

generics

...

are

...

good

...

for

...

type

...

safety

...

throughout

...

your

...

code.

...

They

...

document

...

the

...

code

...

directly

...

with

...

intent:

...

List<Person>

...

leaves

...

little

...

to

...

the

...

imagination

...

as

...

to

...

what

...

goes

...

into

...

the

...

list.

...

These

...

benefits

...

are

...

a

...

great

...

asset

...

to

...

Wicket.

...

Wicket

...

uses

...

the

...

concept

...

of

...

models

...

to

...

provide

...

components

...

with

...

the

...

data

...

that

...

needs

...

to

...

be

...

rendered.

...

Until

...

recently

...

Wicket

...

was

...

Java

...

1.4

...

based,

...

and

...

a

...

lot

...

of

...

components

...

suffered

...

from

...

the

...

lack

...

of

...

generified

...

models.

...

Take

...

for

...

instance

...

the

...

ListView:

Generics Suck!

TBD

Proposed Approaches

No Generics!

IModel<T> Only

IModel<T> and Component<T>

Hybrid: IModel<T> and Select Parts of the Component Hierarchy (ListView, etc.)

This approach will not work because covariant parameter types are not allowed. The "setters" can't be overridden to take a different parameter type.

Decoupld the "default" Model from Component Completely and Let Subclasses Use Their Own Typed Models

Generics are warranted for IModel but too much verbosity for Component

If there is any place in Wicket which clearly holds typed data, it is the models (the IModel interface). In practice, one ends up creating a lot of custom components without models, such as different subclasses of MarkupContainer and possibly Page, and here the Component generification just adds more verbosity to the code.

Typically when starting to use a custom component, you start by typing its constructor call, so the constructor of a custom component is a crucial place for making clean and easy APIs. The possibility of giving type arguments to the IModel instance being passed in there is great, DropDownChoice is a prime example as it also has constructors with a List that needs to contain objects of the same type as the model. However,

Code Block
java
java
}}:

h2. Generics Suck!

TBD

h2. Generics are warranted for {{IModel}} but too much verbosity for {{Component}}

If there is any place in Wicket which clearly holds typed data, it is the models (the {{IModel}} interface). In practice, one ends up creating a lot of custom components without models, such as different subclasses of {{MarkupContainer}} and possibly {{Page}}, and here the {{Component}} generification just adds more verbosity to the code. 

Typically when starting to use a custom component, you start by typing its constructor call, so the constructor of a custom component is a crucial place for making clean and easy APIs. The possibility of giving type arguments to the {{IModel}} instance being passed in there is great, [DropDownChoice|http://svn.apache.org/repos/asf/wicket/trunk/wicket/src/main/java/org/apache/wicket/markup/html/form/DropDownChoice.java] is a prime example as it also has constructors with a List that needs to contain objects of the same type as the model. However, 
{code:java}
DropDownChoice fruit = new DropDownChoice("fruit", new Model<Fruit>(), Arrays.asList(apple, orange, banana));
{code}

is

...

better

...

than

Code Block
java
java

{code:java}
DropDownChoice<Fruit> fruit = new DropDownChoice<Fruit>("fruit", new Model<Fruit>(), Arrays.asList(apple, orange, banana));
{code}

Also

...

when

...

using

...

2.0

...

the

...

experience

...

was

...

that

...

IModel

...

type

...

was

...

excellent,

...

but

...

Component

...

type

...

was

...

more

...

like

...

noise.

...

When

...

using

...

1.3,

...

the

...

lack

...

of

...

types

...

has

...

hardly

...

caused

...

trouble;

...

mostly

...

types

...

would

...

be

...

helpful

...

when

...

first

...

creating

...

a

...

new

...

component.

...

Once

...

you

...

have

...

the

...

component

...

constructed

...

and

...

are

...

calling

...

getModelObject()

...

on

...

it,

...

you're

...

typically

...

sure

...

of

...

what

...

to

...

expect.

...

There

...

have

...

not

...

been

...

difficult

...

problems

...

concerning

...

objects

...

of

...

wrong

...

type

...

inside

...

models.

...

In

...

this

...

approach,

...

using

...

getModelObject()

...

you

...

cast

...

the

...

same

...

as

...

in

...

1.3,

...

and

...

for

...

getModel()

...

you

...

can

...

do

...

the

...

necessary

...

casting

...

cleanly

...

,

...

which

...

leaves

...

you

...

in

...

a

...

situation

...

better

...

than

...

in

...

1.3.

...


(Timo

...

Rantalaiho)

...

I

...

mostly

...

agree

...

with

...

Timo.

...

The

...

only

...

thing

...

we

...

lose

...

from

...

Component

...

is

...

generification

...

of

...

IConverter.

...

Since

...

IConverter

...

is

...

mostly

...

only

...

important

...

in

...

FormComponent

...

perhaps

...

we

...

can

...

fully

...

type

...

FormComponent

...

which

...

would

...

allow

...

us

...

to

...

have

...

typesafe

...

converters

...

AND

...

validators.

...


(Igor

...

Vaynberg)

...

If

...

we

...

dont

...

do

...

Component

...

but

...

we

...

do

...

specific

...

subtypes

...

like

...

FormComponent

...

that

...

the

...

above

...

DropDownChoice

...

example

...

still

...

has

...

that

...

signature..

...


and

...

then

...

i

...

also

...

vote

...

for

...

Repeaters

...

to

...

be

...

generified,

...

because

...

especially

...

in

...

the

...

onPopulate

...

it

...

is

...

very

...

descriptive

...

what

...

you

...

get..

...


But

...

i

...

think

...

people

...

will

...

then

...

start

...

complaining

...

because

...

they

...

are

...

using

...

Links

...

a

...

lot

...

and

...

there

...

model

...

object..

...

So

...

then

...

we

...

do

...

get

...

the

...

question

...

constantly

...

on

...

the

...

list

...

Why

...

is

...

this

...

and

...

Why

...

isnt

...

that...

...

(i

...

think)

...


I

...

still

...

dont

...

find

...

this

...

that

...

bad..

...

It

...

really

...

describes

...

it:

Code Block
java
java

{code:java}
DropDownChoice<Fruit> fruit = new DropDownChoice<Fruit>("fruit", new Model<Fruit>(), Arrays.asList(apple, orange, banana));
{code:java

because what you also could do is this:

Code Block
java
java
}

because what you also could do is this:

{code:java}
DropDownChoice<Fruit> fruit = new DropDownChoice<Fruit>("fruit", Arrays.asList(apple, orange, banana));
{code:java}

and yes i 

and yes i know..

...

this

...

is

...

somewhere

...

not

...

really

...

type

...

safe..because

...

now

...

a

...

CompoundModel

...

is

...

used

...

and

...

that

...

is

...

based

...

on

...

strings..

...


But

...

in

...

the

...

code

...

is

...

more

...

descriptive..

...

but

...

i

...

guess

...

somehow

...

we

...

should

...

have

...

a warning (sad)
And this is also the reason why i am also working on type safe property models.
(Johan Compagner)

Suggestion 1 ---- {}setResponsePage(){} ---signature

The initial issue that started this discussion was a problem with this method signature:

Code Block
 warning :(
And this is also the reason why i am also working on type safe property models.
(Johan Compagner)

h2.  -Suggestion 1 - {{setResponsePage()}} signature-

The initial issue that started this discussion was a problem with this method signature:

{code}
public final void setResponsePage(final Class<? extends Page<?>> cls, PageParameters parameters)
{
    getRequestCycle().setResponsePage(cls, parameters);
}
{code}

We

...

could

...

change

...

this

...

to:

{
Code Block
}
@SuppressWarnings({"unchecked"})
public final void setResponsePage(final Class<?> cls, PageParameters parameters)
{
    final Class<? extends Page<?>> castCls = (Class<? extends Page<?>>) cls;
    getRequestCycle().setResponsePage(castCls, parameters);
}
{code}

This

...

way

...

(a)

...

users

...

migrating

...

to

...

1.4

...

don't

...

get

...

tons

...

of

...

warnings/errors

...

and

...

(b)

...

if

...

they

...

pass

...

in

...

the

...

wrong

...

thing,

...

a

...

class

...

cast

...

exception

...

will

...

be

...

thrown

...

quickly.

...

NOTE:

...

This

...

is

...

just

...

an

...

example

...

-

...

we

...

may

...

wish

...

to

...

move

...

the

...

cast

...

into

...

the

...

getRequestCycle()

...

call

...

...

...

My

...

idea

...

is

...

to

...

dial

...

back

...

the

...

generics

...

on

...

some

...

of

...

the

...

API

...

calls

...

like

...

this

...

one

...

and

...

others

...

that

...

cause

...

pain,

...

but

...

leave

...

the

...

generics

...

for

...

useful

...

things

...

like

...

getModelObject().

...

-Doug Donohoe

...

This is already resolved with the following signature:

Code Block
 Donohoe

-----
This is already resolved with the following signature:
{code}
public final <C extends Page<?>> void setResponsePage(final Class<C> cls, PageParameters parameters)
{
    getRequestCycle().setResponsePage(cls, parameters);
}
{code}

Gerolf Seitz

Suggestion 2 - RestartResponseAtInterceptPageException constructor

I agree with Doug, and with suggestion 1 above on setResponsePage changing to take Class<?>

I think another example is the constructor of RestartResponseAtInterceptPageException (and similar classes) that take a class as an argument.

Code Block

 Seitz

h2.  -Suggestion 2 - RestartResponseAtInterceptPageException constructor-

I agree with Doug, and with suggestion 1 above on setResponsePage changing to take Class<?>

I think another example is the constructor of RestartResponseAtInterceptPageException (and similar classes) that take a class as an argument.

{code}
	public RestartResponseAtInterceptPageException(final Class< ? extends Page< ? >> interceptPageClass)
	{
		if (interceptPageClass == null)
		{
			throw new IllegalStateException("Argument pageClass cannot be null");
		}
		redirectToInterceptPage(interceptPageClass);
	}
{code}

We

...

could

...

change

...

this

...

to:

{
Code Block
}
public RestartResponseAtInterceptPageException(final Class<?> interceptPageClass)
{
	if (interceptPageClass == null || !Page.class.isAssignableFrom(interceptPageClass))
	{
		throw new IllegalStateException("Argument pageClass cannot be null and must inherit from Page.class");
	}
	redirectToInterceptPage(interceptPageClass);
}
{code}

This,

...

for

...

the

...

same

...

reason

...

as

...

suggestion

...

1

...

-

...

it's

...

not

...

very

...

likely

...

that

...

someone

...

is

...

going

...

to

...

pass

...

a

...

non-page-child class into these methods or constructors, so the benefit is minimal, but the strictly generified versions penalize you if your page class is not also generified, which would be confusing to new users.

...

Same as above. -Gerolf Seitz

Suggestion 3 Static factory methods

Code Block
new Label<String>("id", new PropertyModel<String>(object, "property"))

is really quite verbose. I got bored with that and wrote

Code Block

public class into these methods LabelFactory {
	public static <T> Label<T> fromProperty(final String labelId, final Object object, final String propertyName) {
		return new Label<T>(labelId, new PropertyModel<T>(object, propertyName));
	}
}

which made constructing Labels just

Code Block
LabelFactory.fromProperty("id", object, "property")

It would be really nice if wicket contained such factories or had the objects on the methods themselves. It would be really cool to say Label.fromString or Label.fromProperty
-Nik Everett

Generics "Gotchas"

Palette

The current (1.4-m2) Palette class' constructor signature looks like this:

Code Block

public Palette(String id,
               IModel<Collection<T>> model,
	       IModel<Collection<? extends T>> choicesModel,
               IChoiceRenderer<T> choiceRenderer,
               int rows,
	       boolean allowOrder)

This doesn't allow me to use an IModel<List<T>>. How about if we change it to:

Code Block

public Palette(String id,
               IModel<? extends Collection<T>> model,
	       IModel<? extends Collection<? extends T>> choicesModel,
               IChoiceRenderer<T> choiceRenderer,
               int rows,
	       boolean allowOrder)

This appears to make things nicer in my local if I change it.

-James Carman

Application.getHomePage()

Currently, the Application.getHomePage() signature looks like:

Code Block

public abstract Class<? extends Page<?>> getHomePage();

If you choose not to generify your pages, you can't return your home page's class from this method. What if we change the method signature to:

Code Block

public abstract Class<? extends Page> getHomePage();

-James Carman

The above certainly compiles, but does it keep eventual warnings purely on the framework side of things or are generic users possibly confronted with them? Considering that full backwards compatibility is not the goal of Wicket 1.4 (there are some small API breaks as well I believe), maybe it's OK to break getHomePage()? In any case the solution is generally very simple for users (add one <Void>).

So is backwards compatibility very important? Is allowing raw types in all cases very important? I think questions like these need to be answered to get an overall idea of the goal of generics for Wicket.

-Sebastiaan van Erk

Certainly it won't work if you override the method like so:

Code Block

@Override
public Class<? extends Page<?>> getHomePage() {
    // Does not compile!
    return Page.class;
}

But this works just fine if you choose not to generify the Page:

Code Block

@Override
@SuppressWarnings("unchecked")
public Class getHomePage() {
    return Page.class;
}

-David Arnold

Variations of 'public void foo(Class<? extends Component<?>> clazz)'

There seems to be uncertainty about the meaning of the signature 'public void foo(Class<? extends Component<?>> clazz)'

Following code has variations of that signature. It does not compile, it is just for demonstration purposes or as a reference in the Mailing list.

Feel free to change or to add explanations.

Code Block

public class Generix
{
	public static void main(String[] args)
	{
		Foo foo = new Foo();

		// explanation needed
		foo.bar(Component.class);
		// explanation needed
		foo.bar(IntegerComponent.class);
		// explanation needed
		foo.bar(GenericComponent.class);
		// explanation needed
		foo.bar(GenericIntegerComponent.class);

		foo.cly(Component.class);
		foo.cly(IntegerComponent.class);
		foo.cly(GenericComponent.class);
		foo.cly(GenericIntegerComponent.class);

		foo.dol(Component.class);
		foo.dol(IntegerComponent.class);
		foo.dol(GenericComponent.class);
		foo.dol(GenericIntegerComponent.class);

		foo.elm(Component.class);
		foo.elm(IntegerComponent.class);
		foo.elm(GenericComponent.class);
		foo.elm(GenericIntegerComponent.class);

		foo.far(Component.class);
		foo.far(IntegerComponent.class);
		foo.far(GenericComponent.class);
		foo.far(GenericIntegerComponent.class);

		Class<? extends Component<?>> bar = Component.class;
		bar = IntegerComponent.class;
		bar = GenericComponent.class;
		bar = GnericIntegerComponent.class;

		Class<? extends Component> cly = Component.class;
		cly = IntegerComponent.class;
		cly = GenericComponent.class;
		cly = GenericIntegerComponent.class;

		// not a variation of 'Class<? extends Component<?>>'
		// but nice for clearing the picture.

		// Little surprise Component.class is the super bound of Component<?>
		Class<? super Component<?>> carl = Component.class;
		// No surprises here:
		carl = IntegerComponent.class;
		carl = GenericComponent.class;
		carl = GenericIntegerComponent.class;
	}

	public static class Component<T>
	{
	}
	public static class IntegerComponent extends Component<Integer>
	{
	}
	public static class GenericComponent<T> extends Component<T>
	{
	}

	public static class GenericIntegerComponent<T> extends Component<Integer>
	{
	}

	public static class Foo
	{
		// non generic method with parameter of a generic type
		public void bar(Class<? extends Component<?>> clazz)
		{
		}

		// non generic method with parameter of a generic type
		// Generix.Component is a raw type. References to generic type
		// Generix.Component<T> should be parameterized
		public void cly(Class<? extends Component> clazz)
		{
		}

		// generic method
		public <T extends Component<?>> void dol(Class<T> clazz)
		{
		}

		// generic method
		public <T extends Component<?>> void elm(Class<? extends T> clazz)
		{
		}

		// generic method
		public <M, T extends Component<M>> void far(Class<T> clazz)
		{
		}
	}
}
 or constructors, so the benefit is minimal, but the strictly generified versions penalize you if your page class is not also generified, which would be confusing to new users.

-----
Same as above. -Gerolf Seitz