12

I'm really confused: The standard approach in Java is to throw exceptions only in "abnormal" conditions and not to use them to signal end-of-iterator.

examples: Effective Java, item 57 ("Use exceptions only for exceptional conditions") and JavaSpecialists newsletter 162:

Flow control

We should never cause an exception that is otherwise preventable. I have seen code where instead of checking bounds, it is assumed that the data will be correct and then RuntimeExceptions are caught:

Here is an example of bad code (please don't code like this):

public class Antipattern1 {
   public static void main(String[] args) {
     try {
       int i = 0;
       while (true) {
         System.out.println(args[i++]);
       }
     } catch (ArrayIndexOutOfBoundsException e) {
       // we are done
    }
  }
}

whereas it is standard to use this idiom in Python, e.g. StopIteration:

exception StopIteration

Raised by an iterator‘s next() method to signal that there are no further values. This is derived from Exception rather than StandardError, since this is not considered an error in its normal application.

Why is it bad for Java but good for Python?

Jason S
  • 184,598
  • 164
  • 608
  • 970
  • 2
    Both of those rules are really just opinion. There's no objective technical reason behind either. Within a tribe, opinions tend to coalesce into a social norm, but in different tribes, different norms can arise. This is just an example of that. – Tom Anderson Oct 17 '11 at 21:53
  • 1
    @Jason S: can't speak for the Python side but I think that the *"iteration over an array"* may be a bad example to use for your question (nice question btw). The reason it's bad is that wrapping things in *try/catch* block can prevent modern VMs from doing cool optimizations. Joshua Block, in *Effective Java*, also points out that *"It is generally expensive to create, throw and catch an exception"*. So performance-wise it seems really very terrible (Bloch calls it overall "horrible"). From that reason alone it makes the *"array iteration using exception"* seems a bad idea for Java. – TacticalCoder Oct 17 '11 at 22:00
  • @user988052: In Python, setting up a try/except block is very fast, and the raising+catching is pretty fast as well. Both are one-time operations; certainly it's faster than all those `if` statements, at least for more than a few elements. – Petr Viktorin Oct 17 '11 at 22:13
  • @Petr Viktorin: thanks a lot. So if it's blazing fast under Python and possibly awful performance-wise in Java (by *also* preventing JVM optimizations from kicking in, in addition to potentially having try/throw/catch be kinda slow in itself), that seems to be a good reason why, in this *"iteration over an array"*, it's fine in Python but bad in Java. – TacticalCoder Oct 17 '11 at 22:18
  • @user988052: The real reason is readability, prevention of [TOCTTOU](http://en.wikipedia.org/wiki/Time-of-check-to-time-of-use) bugs & race conditions, and the fact that EAFP goes well with duck typing. Python cares about those more than about performance. But it's a nice bonus :) – Petr Viktorin Oct 17 '11 at 22:24
  • Also note that in Java lots of those if() events are necessary anyhow - eg `x.something()` has to check for null internally, so that `if(x != null) x.something()` doesn't add any additional cost (also null checks of that kind are extremely cheap anyhow, there's usually hw support for that). But then exceptions are still rather expensive even in python (you have to create a new object then find the correct exception handler..) compared to a simple if - [small test case](http://pastebin.com/BsMyKBci). – Voo Oct 17 '11 at 22:34
  • @Voo: You're right, EAFP is not generally faster than LBYL; as I said there are other reasons for it. (But do note that to simulate the StopIteration case, you'd have to move the try/except out of the loop.) Also, `if x:` is much faster (as well as more pythonic) than `if x != 0:`. – Petr Viktorin Oct 17 '11 at 23:22
  • @PetrViktorin `if x` and `if x != 0` isn't exactly the same and the google style guide demands the more explicit variant so I'm drilled to it. And the above test is only there to show that exceptions are still much more expensive even in python - but the relative cost to other operations (eg lookup in class dirs) may change things. But still EAFP isn't necessarily about performance (and really python code shouldn't worry about such small differences anyhow) – Voo Oct 18 '11 at 06:09

7 Answers7

7

Python and Java have vastly different approaches to exceptions. In Python, exceptions are normal. Look up EAFP (Easier to ask for forgiveness than permission) in the Python glossary. Also check what Wikipedia has to say.

StopIteration is just an example of EAFP – just go ahead and get the next thing from the iterator, and if that fails, handle the error.

If the code is more readable with a non-local exit, in Python you use an exception. You don't write checks, you just deal with failures if things don't work out. There's absolutely nothing shameful about it, in fact it's encouraged. Unlike in Java.


Now for a specific case of StopIteration: Consider generator functions.

def generator():
    yield 1
    print('Side effect')
    yield 2

To support some kind of has_next() method, the generator would have to check for the next value, triggering the print before the 2 is asked for. The value (or exception raised) would have to be remembered in the iterator. If has_next was called twice, only the first one would trigger the side effect. Or the next value could always be precomputed, even if it's not needed.

I find Python's semantics – computing only whenever the next value is needed – the nicest alternative.

Of course Java doesn't have resumable generators, so it's hard to compare here. But it's some anecdotal evidence that StopIteration generalizes better than hasNext().

Petr Viktorin
  • 65,510
  • 9
  • 81
  • 81
  • Another, and perhaps cleaner, approach to the has_next() method: the generator coult internally set a flag once the end of the function has been reached. This would follow the semantic of only computing the value when requested. – Rocoty Sep 05 '16 at 18:41
  • @Rocoty Not quite - if there are statements between the end of the function and the last yield in the function, such as (potentially) resource cleanup or some sort of logging, the flag won't be set as you seem to expect. Whether this is good practice... is not really the point; I don't think anything could allow the flag approach and allow code after the last yield without changing the point at which generators pause from "on yielding" to "immediately before the next yield after the one actually being yielded". – Vivian Oct 18 '16 at 17:40
  • @DavidHeyman Kotlin 1.1 implements something similar to what I said with coroutines. It computes the value when hasNext() is called, and returns the computed value when next() is called (if the value hasn't been computed, next() will compute it first). hasNext() is (almost) always called in conjunction with next(). Typing this now, though, I realise that this is almost exactly how Python does it, but with a guard rather than an exception. (Asking permission rather than forgiveness) – Rocoty Oct 18 '16 at 18:27
3

There is nothing that stops you from using exceptions like that in java, it just looks ugly, at least to a java developer.

The main reason is that stacktraces from exceptions are expensive, and possibly also that java developers might be slightly more concerned about spending computing resources than Python developers.

Java is also a rather "clean" language - some would say fundamentalistic, which is one of the reasons it's a nice language. (* see comment)

Anyway. Fundamentalists (and some normal people) thinks that using exceptions for normal flow just isn't The Right Way... :-)

But besides that, recent jvm's detects that you are generating a lot of stacktraces for the same spot of code, and will actually throw exceptions without them "after a while" to speed up things.

KarlP
  • 5,149
  • 2
  • 28
  • 41
  • *) The "cleanness" is also the cause of much typing of boiler plate code while battling frameworks and xml-configurations from hell written by a unholy gonglomeration of architecture-astronauts, code-fundamentalists and unix-hackers that seems to like java. I am looking at YOU Maven! Today, I suppose the worst complicators are focusing on newer cooler stuff, and the rest of us are spending time cleaning up the mess they left, like early EJB and so on... – KarlP Oct 17 '11 at 22:26
1

Java sometimes does it, too: "All implementations of DataInput methods use EOFException instead of return values." In this case there's no way to use the usual sentinel value, -1.

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • I'm guesing similar use cases arise in Python. – trashgod Oct 17 '11 at 21:26
  • 1
    Not really — in Python you're not limited to a single return type. You can happily return None from a function that normally returns integers. (You should raise an exception if you want idiomatic Python code, but it's not a technical limitation.) – Petr Viktorin Oct 17 '11 at 21:47
1

The reason its not recommended is because in java, exception handling is generally expensive to process and recover from. When an exception is thrown, it causes a the jvm to go backtrack what is was doing in order to provide a stack trace which is never a good thing for performance. In short, it's language misuse - there will usually be a cleaner, more efficient way of handling logic. Consider the following code:

try {

    int x = service.getValue();

    if(x > 5)
        throw new NumberTooBigException("Too Big");
    else
        throw new NumberTooSmallException("Too Small");

} catch (NumberTooBigException e) {

    System.out.println("i think it's too big...");

} catch (NumberTooSmallException e) {

    System.out.println("i think it's too small...");

}

A better approach is to use just use java's intended control logic:

if(x > 5)
    System.out.println("i think it's too big...");
else
    System.out.println("i think it's too small...");

As you can see from comparing these two snippets, exception handling is kind of ridiculous - overkill for what the sample intends to do. A better approach for the example you posted would be something like this:

String[] args = {"one", "two", "three"};
for(String arg : args)
    System.out.println(args);
}

Exceptions are better used for when things really go wrong, like IOException("no space left on device"), ClassNotFoundException("can't find your code to run") or NoRouteToHostException("Can't connect to host").

0

Exceptions in Java captures execution stack, which is extremely slow: How slow are Java exceptions?

If you use exception in a control flow pass null as Throwable. Otherwise following standard Java librery code will be called through hierarchy of super calls:

public Throwable() {
    fillInStackTrace();
}

public synchronized Throwable fillInStackTrace() {
    if (stackTrace != null ||
        backtrace != null /* Out of protocol state */ ) {
        fillInStackTrace(0);
        stackTrace = UNASSIGNED_STACK;
    }
    return this;
}

private native Throwable fillInStackTrace(int dummy);
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
0

StopIteration exists in Python for simple iteration over any sequence.

Using an exception to implement this was a design choice, and it really isn't that inconsistent with Java's exception mentality. An iterator's next() method being called when there are no more elements is an exceptional condition, and catching that exception behind the scenes in a for loop is a pretty simple way to implement "take items until there aren't any left".

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • 4
    That's an extremely bad comparison. The correct one would be `for (Item i : itr) System.out.println(i)`. In both cases the compiler hides the implementation detail just fine - you can implement the same code fine with or without exceptions usually. All comes down to how you define "exceptional". And python - java/c# just have different paradigms; "wrong" and "right" doesn't make much sense in that context. – Voo Oct 17 '11 at 21:41
  • @Voo - Good point, I haven't coded in Java for a while. I removed the example and left the rest since it is still applicable. – Andrew Clark Oct 17 '11 at 21:43
0

There is no right or wrong answer to this. Exception handling is a neutral control flow construct, and the best use of it depends on context and style.

In this case, both Java and Python do the same thing, for the same reasons: java.util.Iterator's next() uses NoSuchElementException to signal end-of-iterator. This is simply good style, in both languages: the alternative of using a special sentinel return value is is much worse, for various reasons.

As a rule of thumb, you should consider the use of exceptions whenever you write a function that wants to signal more than one kind of control flow return. This includes abnormal error conditions, but good use of exception signalling is certainly not limited to that.

Pi Delport
  • 10,356
  • 3
  • 36
  • 50