views:

76

answers:

2

How do I call start() below?

package com.example.test;

class Bar {}

public class Foo<K>
{
    final private int count;
    final private K key;

    Foo(Builder<K> b)
    {
        this.count = b.count;
        this.key = b.key;
    }

    public static class Builder<K2>
    {
        int count;
        K2 key;

        private Builder() {}
        static public <K3> Builder<K3> start() { return new Builder<K3>(); }
        public Builder<K2> setCount(int count) { this.count = count; return this; }
        public Builder<K2> setKey(K2 key) { this.key = key; return this; }
        public Foo<K2> build() { return new Foo(this); }
    }

    public static void main(String[] args)
    {
        Bar bar = new Bar();
        Foo<Bar> foo1 = Foo.Builder.start().setCount(1).setKey(bar).build();
        // Type mismatch: cannot convert from Foo<Object> to Foo<Bar>

        Foo<Bar> foo2 = Foo.Builder<Bar>.start().setCount(1).setKey(bar).build();
        // Multiple markers at this line
        // - Bar cannot be resolved
        // - Foo.Builder cannot be resolved
        // - Syntax error on token ".", delete this token
        // - The method start() is undefined for the type Foo<K>
        // - Duplicate local variable fooType mismatch: cannot convert from Foo<Object> to Foo<Bar>

        Foo<Bar> foo3 = Foo<Bar>.Builder.start().setCount(1).setKey(bar).build();
        // Multiple markers at this line
        // - Foo cannot be resolved
        // - Syntax error on token ".", delete this token
        // - Bar cannot be resolved     
    }
}
+4  A: 

You were close:

Foo.Builder.<Bar> start().setCount(1).setKey(bar).build();

Cheers! :)

P.S. If the compiler can't infer the type parameter of the method on its own, you can force it by calling obj.<Type> method(...) .

P.P.S you might want to use:

public Foo<K2> build() {
    return new Foo<K2>(this);
}

Avoid using raw types.

Andrei Fierbinteanu
every time you write down an explicit type argument in a method, god kills a kitten ;)
sfussenegger
aha, so that's how you specify static methods with generic types. thanks!
Jason S
@sfussenegger: ok, so is there another way to do this?
Jason S
@Jason: supply the key to the start() method and pass it on to the private constructor, so that you get something like: Bar bar = new Bar(); Foo.Builder.start(bar); // type will be inferred from bar
perp
@sfussenegger You caught me, I'm not a big fan of cats :P
Andrei Fierbinteanu
@Andrei not being a fan doesn't justify killing kittens, nothing does! ;)
sfussenegger
hmmm... so why is writing down an explicit type argument bad? It doesn't seem like there's an alternative that's really any less ugly.
Jason S
@Jason I think the kitten quote was originally from Josh Bloch. I use it myself though, at least once in a while. If you only use this builder yourself, just go for it. However, if there are other developers, my answer could be easier for them. Explicit type arguments simply aren't pretty and - even more important - not everybody developer knows they exist. Simply look at you. You had to come here in order to get them to work. Similarly, other developers will struggle to use your library that makes use of them. So finally, it boils down to a matter of usability and taste.
sfussenegger
ok, so it's less a matter of ugliness/messiness than of obscure/confusing syntax. thanks!
Jason S
+2  A: 

Andrei's method is okay, but most programmers will likely struggle with the rather unknown syntax. It might be easier to use this way:

static public <K3> Builder<K3> start(Class<K3> cls) { return new Builder<K3>(); }

Foo<Bar> foo1 = Foo.Builder.start(Bar.class).setCount(1).setKey(bar).build();

The class is only passed to help with the generic type. It's not pretty, but at least the syntax is common knowledge.

sfussenegger