-3

i m playing around with Java Streams and I wonder if there is any way to create a code Block like this ->

if(givenString.equals("productA")) {
    return new productA();
} else if(givenString.equals("productB") {
    return new productB();
} .....

into a Java Stream like this ->

Stream.of(givenString)
      .filter(e -> e.equal("productA)")
      .map(e -> new productA())   

i came across with this solution which works but i m not convinced...

Stream.of(givenString)
      .map(e -> e -> e.equals("productA)" ? new productA() : new productB())      
      .findAny()
      .get()
Jaime S
  • 1,488
  • 1
  • 16
  • 31
user11998955
  • 17
  • 1
  • 5
  • 6
    Why you want to use Stream to do that simple task. What is the problem with your first approach ? – Amit Bera Aug 30 '19 at 11:27
  • Also, what element of the approach are you 'not convinced' about. You say it works, so I'm not sure what to suggest to fix it. – BeUndead Aug 30 '19 at 11:28
  • Well i don t have a problem with that...i just want to explore the possibilities of streams.. – user11998955 Aug 30 '19 at 11:29
  • I think your are confusing the use of streams with conditional checking. From question it seems like you have a string where you need to check the value so if else block or switch case will be beast for those. Even though you are using java 8 or above there is no rule that everything will be in the steams or some new features. – mallikarjun Aug 30 '19 at 11:32
  • 2
    Stream isn't a replacement for logical conditions. In your particular case what you've actually got is a `Map>` rather than a stream. – M. Prokhorov Aug 30 '19 at 11:32
  • what do you mean by Map>? – user11998955 Aug 30 '19 at 11:39
  • @user11998955 If I may, M. Prokhorov is talking about using a `Supplier` functional interface to allow you to create an instance of the proper class and assign it to a variable of the proper type. Not a bad idea, actually. – WJS Aug 30 '19 at 14:40
  • Also, if you have to check for equality with several strings, consider using a `switch` statement. – Johannes Kuhn Aug 30 '19 at 14:59

2 Answers2

2

You don't want to do that inline in a stream. Instead, write a helper method that does just that:

private static Product createByString(String name) {
    // I assume Product is a common superclass
    // TODO: implement
}

Now the question is: How should this method be implemented?

  1. Use a big switch statement.

    private static Product createByString(String name) {
        switch (name) {
            case "productA": new productA();
            case "productB": new productB();
            // ... maybe more?
            default: throw new IllegalArgumentException("name " + name + " is not a valid Product");
        }
    }
    

    Pro: a switch on a string is compiled into a jumptable, so you won't have n string comparisons.
    Con: You can't extend it at runtime, and you have to keep this method in sync.

  2. Use a HashMap<String,Supplier<Product>>.

    private static final Map<String,Supplier<Product>> productConstructors = new HashMap<>();
    static {
        productConstructors.put("productA", productA::new);
        productConstructors.put("productB", productB::new);
    }
    private static Product createByString(String name) {
        Supplier<Product> constructor = productConstructors.get(name);
        if (constructor == null) {
            // Handle this?
            throw new IllegalArgumentException("name " + name + " is not a valid Product");
        }
        return constructor.get();
    }
    

    Pro: with some easy modifications you can add new products to this implementation, or even replace them.
    Con: has some moderate overhead, and you still need to maintain a the mapping between "productA" and it's type.

  3. Use reflection.
    The good old hammer where every problem looks like a nail.

    private static Product createByString(String name) {
         try {
             return Class.forName("your.pkgname. " + name).asSubclass(Product.class).getConstructor().newInstance();
         } catch (ReflectiveOperationException e) {
             throw new RuntimeException(e);
         }
    }
    

    Pro: You don't need to do the binding.
    Con: It's slow.

Johannes Kuhn
  • 14,778
  • 4
  • 49
  • 73
0

In your first example below:


    if(givenString.equals("productA")) {
        return new productA();
    } else if(givenString.equals("productB") {
        return new productB();
    } 

You are returning an instance of some object specified via a string. It seems to me that if you know the string, you can just create the object right away without using an intervening method call to do so.

Another possibility is that the class name was provided via some user input. In this case you might want to consider reflection to accomplish this so you can reference the methods and fields of the newly created class.

In either case I doubt streams is a reasonable approach for this sort of requirement.

WJS
  • 36,363
  • 4
  • 24
  • 39