views:

74

answers:

3

I was trying for days to figure out if is possible, I failed but maybe it is be possible(I think it should be possible).

Let's say we have a few UI components similar with Swing hierarchies + we will use fluent interfaces Fluent Interfaces:

public abstract class Component {

     ...

     public abstract Component setName(String name);

     public abstract String getName();

     ...

}

public abstract class Panel extends Component {
     ....
}

public abstract class TitledPanel extends Panel {
     ....

     public abstract TitledPanel setTitle(String title);

     public abstract String getTitle();
} 

Is it possible, using generic to be able to write something like that?

new TitledPanel().setName("panel").setTitle("Title);

setName should return a TitledPanel instead of Component to be able to link those calls.

This is just a simple example, but the idea is once I have an object of type T any call to any fluent method of a superclass to return type T.

Edit 1: I forgot to exclude the part with overriding the methods and returning a covariant types :) I want just plain generics, if it is possible indeed.

+1  A: 

I am not really sure if you could achieve this with generics. What you can do is something like this:

public abstract class Component {

private String name;

public Component setName(String name) {
    this.name = name;
    return this;
}

public String getName() {
    return name;
}

}

public abstract class Panel extends Component {

}

public class TitledPanel extends Panel {

private String title;

public TitledPanel setTitle(String title) {
    this.title = title;
    return this;
}

@Override
public TitledPanel setName(String name) {
    super.setName(name);
    return this;
}

public String getTitle() {
    return title;
}

}

Now new TitledPanel().setName("panel").setTitle("Title"); will work

Lombo
+2  A: 

Firstly I suggest using only set methods, and dropping the set.

You can use covariant return types, but this means overriding every method in each derived class. However, it does involve a lot of very tedious code.

public abstract class Component {
    ...
    public Component name(String name) {
        this.name = name;
        return this
    }
}

public abstract class Panel extends Component {
    ...
    public Panel name(String name) {
        super.name(name);
        return this;
    }
}

Adding a generic THIS parameter, as enum, makes the implementation easier, but client code may need to add a <?> in declarations.

public abstract class Component<THIS extends Component<THIS>> {
    ...
    protected abstract THIS getThis();
    ...
    public THIS name(String name) {
        this.name = name;
        return this
    }
}

public abstract class Panel<THIS extends Panel<THIS>> extends Component<THIS> {
    ...
}

public class TitledPanel extends Panel<TitledPanel> {
    ...
    public TitledPanel getThis() {
        return this;
    }
}

An alternative is to use the Double Brace Idiom.

new TitledPane() {{
    name("panel");
    title("Title");
}}
Tom Hawtin - tackline
"Firstly I suggest using only set methods, and dropping the set." - didn't understood this part.
adrian.tarau
Which part? I suggest not having get methods - they are useless. I also suggest not using the `set` prefix because it is ugly and unnecessary.
Tom Hawtin - tackline
You need to have get methods to have access of those members. Well, set and get makes sense and it is also a naming convention from Sun: http://java.sun.com/docs/codeconv/html/CodeConventions.doc8.htmlNot that Sun doesn't have any broken rules, but these simple rules makes sense, at least for me.
adrian.tarau
Those members should be accessed from within the instance. `set` without `get` is just noise.
Tom Hawtin - tackline
Think about Component as java.awt.Component, there must be public methods to access the component members: bounds, name, layout, etc.Anyway, it doesn't matter :) could this take advantage of generics, thats my question?If not I would say they did something wrong :) it should be possible that setName the same type(TitledPanel) and not a super type(Component) since the call is make on a TitledPanel.
adrian.tarau
+1  A: 

In your TiltedPanel (which can't be abstract by the way, if you want to new one up), you can override the abstract methods in Component and change the return type. The return type is not part of the method signature, so you can implement it with a different return type:

public class Test
{
    public static void main(String[] args)
    {
        TiltedPanel p = new TiltedPanel().setName("panel").setTitle("title");
        System.out.println("name = " + p.getName());
        System.out.println("title = " + p.getTitle());
    }
}

abstract class Component
{
    public abstract Component setName(String name);

    public abstract String getName();
}

abstract class Panel extends Component
{
}

class TiltedPanel extends Panel
{
    private String title;
    private String name;

    public TiltedPanel setName(String name)
    {
        this.name = name;
        return this;
    }

    public String getName()
    {
        return this.name;
    }

    public TiltedPanel setTitle(String title)
    {
        this.title = title;
        return this;
    }

    public String getTitle()
    {
        return this.title;
    }
}
Andy White
Indeed, it is possible, but i would like it without covariant types :)
adrian.tarau