43

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     
    }
}
Jason S
  • 184,598
  • 164
  • 608
  • 970

4 Answers4

47

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
  • 7,656
  • 3
  • 31
  • 45
  • 3
    every time you write down an explicit type argument in a method, god kills a kitten ;) – sfussenegger Jul 08 '10 at 14:36
  • aha, so that's how you specify static methods with generic types. thanks! – Jason S Jul 08 '10 at 14:37
  • @sfussenegger: ok, so is there another way to do this? – Jason S Jul 08 '10 at 14:37
  • 2
    @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 Jul 08 '10 at 14:42
  • @sfussenegger You caught me, I'm not a big fan of cats :P – Andrei Fierbinteanu Jul 08 '10 at 15:03
  • @Andrei not being a fan doesn't justify killing kittens, nothing does! ;) – sfussenegger Jul 08 '10 at 15:06
  • 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 Jul 08 '10 at 15:09
  • 2
    @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 Jul 08 '10 at 17:47
  • 1
    ok, so it's less a matter of ugliness/messiness than of obscure/confusing syntax. thanks! – Jason S Jul 08 '10 at 22:58
29

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.

Another option would be to start right away with an object of the generic type:

Foo<Bar> foo1 = Foo.Builder.startWithKey(bar).setCount(1).build();
sfussenegger
  • 35,575
  • 15
  • 95
  • 119
29

If you are a fan of lombok project and using its annotation @Builder to achieve builder pattern then you can remove all the unnecessary code like you are writing and do it in less code.

Example:

@Builder
public class ErrorResponse<T> {
    private String message;
    private List<String> reasons;
    private List<String> details;
    private T data;
}

And you can initialize it like:

ErrorResponse<MyData> myDataError = ErrorResponse.<MyData>builder()
.message("My message")
.build();
Yubaraj
  • 3,800
  • 7
  • 39
  • 57
3

here's how I would do :

package odmor2018.krit.rtti.builderpattern;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

public class Person {

    private String firstName;
    private String middleName;
    private String lastName;
    private boolean sex;

    public Person(String firstName, String middleName, String lastName, boolean sex) {
        this.firstName = firstName;
        this.middleName = middleName;
        this.lastName = lastName;
        this.sex = sex;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" + "firstName=" + firstName + ", middleName=" + middleName + ", lastName=" + lastName + ", sex=" + sex + '}';
    }

    public static class Builder {

        private final Field[] fields = Person.class.getDeclaredFields();
        private final List<Field> fieldsList = Arrays.asList(fields);
        private final List<String> fNames = fieldsList.stream().map(f -> f.getName()).collect(Collectors.toList());

        private final Person nP = new Person();

        public Builder with(String fName, Object value) {
            if (fNames.contains(fName)) {
                int fInd = fNames.indexOf(fName);
                try {
                    Field f = fields[fInd];
                    f.setAccessible(true);
                    f.set(nP, value);
                } catch (Exception ex) {
                    Logger.getLogger(Person.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return this;
        }

        public Person createPerson2() {
            return nP;
        }
    }

    public static void main(String[] args) {
        Person p3 = new Person.Builder()
                .with("firstName", "doooobri2")
                .with("sex", false)
                .createPerson2();
        System.err.println("p3:" + p3);
    }
}
dobrivoje
  • 848
  • 1
  • 9
  • 18
  • 1
    Ok got you dynamic builder with reflection! Be aware that you can use lombock for that one https://projectlombok.org/features/Builder – gkatzioura Oct 24 '18 at 10:13