-3

Is method chaining good?

I am not against functional programming that uses method chaining a lot, but against a herd mentality where people mindlessly run behind something that is new.

The example, if I am processing a list of items using stream programming and need to find out the exact row that resulted into throwing NullPointerException.

private void test() {
    List<User> aList = new ArrayList<>();
    // fill aList with some data
    aList.stream().forEach(x -> doSomethingMeaningFul(x.getAddress()));
}

private void doSomethingMeaningFul(Address x)   {
     // Do something
}

So in the example above if any object in list is null, it will lead to NullPointerException while calling x.getAddress() and come out, without giving us a hook to identify a User record which has this problem.

I may be missing something that offers this feature in stream programming, any help is appreciated.

Edit 1: NPE is just an example, but there are several other RuntimeExceptions that could occur. Writing filter would essentially mean checking for every RTE condition based on the operation I am performing. And checking for every operation will become a pain.

To give a better idea about what I mean following is the snippet using older methods; I couldn't find any equivalent with streams / functional programming methods.

List<User> aList = new ArrayList<>();
// Fill list with some data
int counter = 0;
User u = null;

try {
      for (;counter < aList.size(); counter++) {
          u = aList.get(counter);
          u.doSomething();
          int result = u.getX() / u.getY();
      }
} catch(Exception e)  {
  System.out.println("Error processing at index:" + counter + " with User record:" + u);
  System.out.println("Exception:" + e);
}

This will be a boon during the maintenance phase(longest phase) pointing exact data related issues which are difficult to reproduce.
**Benefits:**
- Find exact index causing issue, pointing to data
- Any RTE is recorded and analyzed against the user record
- Smaller stacktrace to look at
  • 1
    You should probably filter out null values first, and do a `map` instead of a `forEach` if you want a bit more functional style -- your `doSomething` would be better off returning the result instead of putting it somewhere hidden. – vlumi Jun 18 '19 at 13:51
  • How to do it with stream programming is something I am looking for. – Nitin Dubey Jun 18 '19 at 13:51
  • @NitinDubey You are already using `stream` but to no use. Maybe look into `filter(Objects:nonNull)` – Naman Jun 18 '19 at 13:54
  • If you just want an index to put in an error object: https://stackoverflow.com/questions/18552005/is-there-a-concise-way-to-iterate-over-a-stream-with-indices-in-java-8 – slim Jun 18 '19 at 14:33

4 Answers4

1

Is method chaining good?

As so often, the simple answer is: it depends.

When you

  • know what you are doing
  • are be very sure that elements will never be null, thus the chance for an NPE in such a construct is (close to) 0
  • and the chaining of calls leads to improved readability

then sure, chain calls.

If any of the above criteria isn't clearly fulfilled, then consider not doing that.

In any case, it might be helpful to distribute your method calls on new lines. Tools like IntelliJ actually give you advanced type information for each line, when you do that (well, not always, see my own question ;)

From a different perspective: to the compiler, it doesn't matter much if you chain call. That really only matters to humans. Either for readability, or during debugging.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
0

In your test() function you are creating an emptylist List<User> aList = new ArrayList<>();

And doing for each on it. First add some element to

aList

If you want to handle null values you can add .filter(x-> x != null) this before foreach it will filter out all null value

Below is code

private void test() {
        List<User> aList = new ArrayList<>();

        aList.stream().filter(x-> x != null).forEach(x -> doSomethingMeaningFul(x.getAddress()));
}

private void doSomethingMeaningFul(Address x)   {
        // Do something
}
rohit prakash
  • 565
  • 6
  • 12
  • That means we would be processing the list twice, so it will have performance impact. – Nitin Dubey Jun 18 '19 at 13:54
  • Why call forEach() on a stream, and not map? – GhostCat Jun 18 '19 at 13:58
  • @NitinDubey first you are filtering null from lis then you are doing processing – rohit prakash Jun 18 '19 at 14:00
  • @NitinDubey If you donot want to filter null values then add if condition inside your forEach and first check if(x != null) then call your function doSomethingMeaningFul(x.getAddress) – rohit prakash Jun 18 '19 at 14:02
  • 1
    @NitinDubey no, this does not process the list twice. You wrap the list in a stream. `forEach()` pulls an item from `filter()`. `filter()` pulls items from the stream and only yields an element to `forEach()` if it passes the predicate. – slim Jun 18 '19 at 14:02
  • @slim got it thanks. I have edited the question to bring more clarity. – Nitin Dubey Jun 18 '19 at 14:17
0

You can write a black of code in streams. And you can find out the list item which might result in NullPointerException. I hope this code might help

private void test() {
    List<User> aList = new ArrayList<>();
    aList.stream().forEach(x -> {
        if(x.getAddress() != null)
            return doSomethingMeaningFul(x.getAddress())
        else
            system.out.println(x+ "doesn't have address");
    });
}

private void doSomethingMeaningFul(Address x)   {
 // Do something
}

If you want you can throw NullPointerException or custom excption like AddressNotFoundException in the else part

Pradyskumar
  • 296
  • 1
  • 3
  • 15
0

There are a few aspects to this.

1) Nulls

It's best to avoid the problem of checking for nulls, by never assigning null. This applies whether you're doing functional programming or not. Unfortunately a lot of library code does expose the possibility of a null return value, but try to limit exposure to this by handling it in one place.

Regardless of whether you're doing FP or not, you'll find you get a lot less frustrated if you never have to write null checks when calling your own methods, because your own methods can never return null.

An alternative to variables that might be null, is to use Java 8's Optional class.

Instead of:

public String myMethod(int i) {
     if(i>0) {
          return "Hello";
     } else {
          return null;
     }
}

Do:

public Optional<String> myMethod(int i) {
     if(i>0) {
          return Optional.of("Hello");
     } else {
          return Optional.empty();
}

Look at Optional Javadoc to see how this forces the caller to think about the possibility of an Optional.empty() response.

As a bridge between the worlds of "null represents absent" and "Optional.empty() represents absent", you can use Optional.ofNullable(val) which returns Empty when val == null. But do bear in mind that Optional.empty() and Optional.of(null) are different values.

2) Exceptions

It's true that throwing an exception in a stream handler doesn't work very well. Exceptions aren't a very FP-friendly mechanism. The FP-friendly alternative is Either -- which isn't a standard part of Java but is easy to write yourself or find in third party libraries: Is there an equivalent of Scala's Either in Java 8?

public Either<Exception, Result> meaningfulMethod(Value val) {
    try {
       return Either.right(methodThatMightThrow(val));
    } catch (Exception e) {
       return Either.left(e);
    }
}

... then:

List<Either<Exception, Result>> results = listOfValues.stream().map(meaningfulMethod).collect(Collectors.toList());

3) Indexes

You want to know the index of the stream element, when you're using a stream made from a List? See Is there a concise way to iterate over a stream with indices in Java 8?

slim
  • 40,215
  • 13
  • 94
  • 127