-1

I would like to create builder chain where I can provide different sets of methods based on the value of i. If I can use these intermediate builder classes then I can limit the available methods at compile-time rather than relying on runtime checks. (The intermediate builder classes aren't intended to be exposed to the end user, they're just there to provide different sets of methods after calling set().)

public class SomeClass {
    public static class SomeClassBuilder {
        public <What should I return ?> set(int i) {
            switch (i) {
               case 1:
                   return new Case1Builder();
                   break;
               case 2:
               case 3:
                   return new Case2And3Builder();
                   break;
               case 4:
                   return new Case4Builder();
                   break;
               default:
                   return this;
            }
        }

        public SomeClass.SomeClassBuilder build() {
        }
    }

    public static final class Case1Builder() {
        public SomeClass.Case1Builder set1() {
        }
    }

    public static final class Case2And3Builder() {
        public SomeClass.Case1Builder set2And3() {
        }
    }

    public static final class Case4Builder() {
        public SomeClass.Case1Builder set4() {
        }
    }
}

And then from my driver class, I could do below:

final SomeClass someClass = new SomeClass.SomeClassBuilder(1).set1().build();
final SomeClass someClass = new SomeClass.SomeClassBuilder(2).set2And3().build();

Is it possible? Maybe using generics?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • What you think you want to do is not possible. I suggest revisiting your design as there is likely a much better way to accomplish your end goal. – Zephyr Mar 14 '19 at 00:44
  • isn't it more like **factory** instead of **builder**? – Lei Yang Mar 14 '19 at 01:01
  • Please clarify what you're trying to accomplish and engage is discourse instead of downvoting everyone who doesn't give you an answer you don't like. – Bob Liberatore Mar 14 '19 at 01:08
  • No, it's a legitimate attempt to be a builder. The goal is to restrict the set of methods that are available at compile-time rather than having runtime exceptions if you call the wrong ones. The intermediate builder classes aren't intended to be exposed to the end user, they're just there to limit the set of methods available. It's a laudable goal and in no way makes this 'not a builder'. – John Kugelman Mar 14 '19 at 01:10
  • The downvotes aren't from the OP, they're mine, and I've explained them. – John Kugelman Mar 14 '19 at 01:12
  • Every single class is public. How are these not exposed to the user? Also, please read SO's policy on Downvoting: https://stackoverflow.com/help/privileges/vote-down – Bob Liberatore Mar 14 '19 at 01:15
  • They have to be public, that's how this design pattern works. Look at the example code the OP provided. `new SomeClass.SomeClassBuilder(2).set2And3().build()` doesn't require the end user to directly reference `Case2And3Builder`; it's hidden behind the chain of fluent method calls. – John Kugelman Mar 14 '19 at 01:18
  • 1
    I mean, sure, except that that can't actually be accomplished, and the set1() and set2And3() methods, since they have to be public, could easily be wrapped into the build() functions. I mean, really, the answer is no, this can't be accomplished (and is therefore not a Builder, or any other pattern). – Bob Liberatore Mar 14 '19 at 01:23
  • This is no Builder at all. A Builder constructs an object. The Builder is another class than the object to build. It holds an instance variable of the type to build and every builder method sets anything and returns 'this', except one build method that returns the constructed object when you are done. – A K Mar 14 '19 at 00:35
  • It's a builder split into several classes. Look at the example usage. It creates a builder, one or more methods are called, and a final `build()` is invoked. That's a builder. – John Kugelman Mar 14 '19 at 00:39
  • It's an horrible nested static class, the top class shoud have no knowledge of it's construction process, well it goes way in the right direction but - eh no :D – A K Mar 14 '19 at 00:43
  • It's quite common to have nested builders. In the Java standard library see [Stream.Builder](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html), [Locale.Builder](https://docs.oracle.com/javase/8/docs/api/java/util/Locale.Builder.html), etc. Guava has [ImmutableList.Builder](https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/ImmutableList.Builder.html), [MinMaxPriorityQueue.Builder](https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/MinMaxPriorityQueue.Builder.html), ... – John Kugelman Mar 14 '19 at 00:46
  • Yeah and it misses completely the goal in regards to dependencies... Look I have an interface (not a class) and serveral outcome possibilities, than my independent builder can create all of these with their internal polymorphic behavior and I get the implementation I want without even knowing a specific class and without calling anything on it – A K Mar 14 '19 at 00:56
  • @Project Sorry for misunderstanding. I called class 'Builder'; sure, but I was not suggesting that this could be only accomplished by Builder pattern. I did not down vote. I never do it. I don't believe in it because every discussion/comment is worth reading to learn and improve knowledge. – Arjun Patel Mar 29 '19 at 17:43

2 Answers2

1

It's not possible. You can return one of several types, but since return types are compile-time you have to declare a supertype of those types. That would be Object, here. Obviously, returning Object isn't useful.

You can't return one of three types and expect the compiler to be able to provide access to different methods based on which of the three is returned. The actual returned type is a runtime artifact, while the list of methods you can call is a compile-time decision.

If you want to do it you'll need multiple set methods.

public Case1Builder set1();
public Case2And3Builder set2Or3();
public Case4Builder set4();
public GenericCaseBuilder setOther();
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
-1

To do things the way you're trying to accomplish them, you'd want to do something like this:

public class SomeClass {
    public static class SomeClassBuilder implements CaseBuilder {
        public CaseBuilder set(int i) {
            switch (i) {
               case 1:
                   return new Case1Builder();
                   break;
               case 2:
               case 3:
                   return new Case2And3Builder();
                   break;
               case 4:
                   return new Case4Builder();
                   break;
               default:
                   return this;
            }
        }

        @Override
        public SomeClass build() {...}
    }

    public static final class Case1Builder() implements CaseBuilder {
        @Override
        public SomeClass build() {...}
    }

    public static final class Case2And3Builder implements CaseBuilder {
        @Override
        public SomeClass build() {...}
    }

    public static final class Case4Builder implements CaseBuilder {
        @Override
        public SomeClass build() {...}
    }


   public interface CaseBuilder {
      public SomeClass build();
   }
}

However, this is a lot more complicated than it needs to be, and isn't really a Builder pattern. I think what you're looking for is actually a Factory pattern, or possibly a FactoryFactory.

Bob Liberatore
  • 860
  • 10
  • 24
  • This doesn't let the user call different `set` methods based on the value of `i`. It just exposes different `build()` methods, which misses the point and doesn't require different intermediate builder objects at all. – John Kugelman Mar 14 '19 at 01:07
  • Then please clarify what you're trying to accomplish. This is the closest thing to what you *could* do. Otherwise, the simple answer is 'no.' – Bob Liberatore Mar 14 '19 at 01:09