开发者

Java generics + Builder pattern

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> foo开发者_JS百科3 = 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     
    }
}


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'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();


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();


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);
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜