Java: Border Cases

The mysubstr function that you implemented in the last lesson contains many errors. "But she passed the test!". Yes, but in these checks there were no so-called ** border cases **. The function worked normally with normal arguments, but how will it behave if you pass such lengths to it?

  • 0
  • A negative number
  • Number greater than the actual size of the string

The mysubstr function is not designed for such options. You might think that this is not a problem: the function works under normal conditions, and you simply do not need to pass on to it “bad” arguments. In an ideal world, yes, but in the real world, your code will run in different situations, with different combinations of conditions and data. The one who runs it will most likely not read it carefully, and even the documentation on it, if you write it, it may not read carefully. Therefore, it will not know exactly what your code will work with and what it will not; and he can simply be mistaken. That is, one cannot be sure that the arguments will always be correct.

Errors in borderline cases are the most common cause of logical errors in programs. Programmers always forget to take something into account. Such errors often manifest themselves not immediately, and may not lead to visible problems for a long time. The program continues to work, but at some point it appears that there are errors in the results.

The ability to cope with such errors comes with experience, through constant jambs in the style of “oh, forgot to check for an empty line!”.

Therefore it is necessary to take into account various special cases, within the framework of common sense. Taking into account means, for example, to try to avoid data corruption, which, according to the logic of the operation, should not have changed; and also, instead of “silently” doing something meaningless or nothing at all, somehow report the problem, and thereby help the calling programmer, telling him that he passed inappropriate arguments.

For example, if a function removes from a string all characters with indices from N and further, where N is the function argument, then we could write a function so that if suddenly it was given the value N<0, then the function is already the first (with index 0) the character considers superfluous and deletes everything. But it's not a fact that it is reasonable: the transfer of a negative number to this function almost certainly indicates a programmer's error, and instead of spoiling the line and continuing to work as it should, it is better to generate an error that indicates to the programmer that caused us did something wrong. We are not talking about how to signal errors in these lessons, but at least, without even knowing it, we could add to the function the first action request from the character string with the index N-1, and then when trying to do this with N < 0 the execution of the function will fail with an error. And we can consider some other cases of incorrect indication of arguments as harmless. For example, we can decide that specifying an index greater than the length of a line, we need to understand so that we don’t need to delete anything - let's say we want to give the calling programmer the ability to cut several lines to the same maximum length he needs without forcing check every line of it: is it already shorter than it needs? But even if we decided that such a borderline case is correct, we did it anyway, first considering it - that is, you always have to ask yourself, and which unexpected values for the arguments we can pass on to us.

Note that it was possible to write a function and in such a way that it goes in a loop from the index N upwards until the loop counter becomes equal to the length of line -1. And then, if suddenly, at the input filed N greater than the length of the string, then the loop counter will never become equal to the length of row-1, and the loop will become eternal. No, in fact, it will not, because if you try to increase the counter above the maximum allowable integer value in Java, an error will occur; but this is not our merit, the compiler saved us, adding such checks automatically. But still there is nothing good if the body of the cycle has time to execute 2 billion times before. And if it displayed the current value of the counter?

Let's imagine the extended function mysubstr. It takes three arguments: a string, an index, and a length of the extracted substring. The function returns a substring of the specified length starting from the specified index. Call examples:

var str = "If I look back I am lost";
mysubstr(str, 0, 1); // => 'I'
mysubstr(str, 3, 6); // => 'I look'

Let's figure out what could go wrong. What ** borderline cases ** worth considering:

  • Negative length of the extracted substring
  • Negative given index
  • The specified index goes beyond the boundary of the entire line.
  • The length of the substring in the amount with the specified index goes beyond the boundary of the entire line

In the implementation of the function, each border case will be a separate piece of code, most likely implemented with if.

To write the mysubstr function and protect against these cases, it is worth writing a separate function that will check the arguments for correctness. Let's do this in the assignment.


Implement the predicate function isArgumentsForSubstrCorrect, which takes three arguments:

  1. line
  2. index from which to start extraction
  3. the length of the extracted substring

The function returns false if at least one of the conditions is true:

  • Negative length of the extracted substring
  • Negative given index
  • The specified index goes beyond the boundary of the entire line.
  • The length of the substring in the amount with the specified index goes beyond the boundary of the entire line

Otherwise, the function returns true.

Don't forget that indexes start with 0, so the index of the last element is “the length of the string minus 1”.

Call example:

var str = "Sansa Stark";
isArgumentsForSubstrCorrect(str, -1, 3); // => false
isArgumentsForSubstrCorrect(str, 4, 100); // => false
isArgumentsForSubstrCorrect(str, 10, 10); // => false
isArgumentsForSubstrCorrect(str, 11, 1); // => false
isArgumentsForSubstrCorrect(str, 3, 3); // => true
isArgumentsForSubstrCorrect(str, 0, 5); // => true

If you got stuck and don't know what to do, you can ask a question in our huge and friendly community