4

Yes I know how to fix this. I was looking at the API between String Class and String Builder Class. Here's what I question:

StringBuilder sb = new StringBuilder("wibble");
String s = new String("wobble");

sb.charAt(index) //Can throw Index Out Of Bounds Exception
s.charAt(index) //Can throw Index Out of Bounds Exception

sb.substring(start, end) //Can throw STRING index out of bounds exception
s.substring(start, end) //Only throws Index out of Bounds Exception (no String)

So what doesn't make sense is when it decides to throw a StringIndexOutOfBoundsException vs an IndexOutOfBoundsException. Yes I know StringIndex is a sub exception. In both String and StringBuilder, some methods that have the same implementation throw the same exception, while in some methods that have the same implementation, StringBuilder throws the sub exception. So why is that?

To save people scrolling through the answers and thanks to the 2 people who answered even though I can only select one as answered, both helped clarify: Java Oracle Doc People made a typo in substring for String. It is supposed to be StringIndexOutOfBoundsException. Same with CharAt() in StringBuilder

4
  • 1
    also why is it substring(...) and subString(...) Commented Mar 11, 2019 at 15:45
  • @RNJ fixed it brosky Commented Mar 11, 2019 at 15:56
  • maybe same reason why String#codePointBefore(0) or String#substring(-1) effectively throws a StringIndexOutOfBounds but documentation only states IndexOutOfBounds - the programmers were just humans Commented Mar 11, 2019 at 15:57
  • @CarlosHeuberger I don't really know the codePointBefore method, but even if it is the same reason... what is that reason... so I could ask the same question about that. It does not seem arbitrary... Commented Mar 11, 2019 at 15:58

2 Answers 2

1

This comes down to which JDK implementation you are using.

I did some digging and found the implemented method, public char charAt(int index) for the StringBuilder class. (Note: I am using Oracle JDK 1.8).

So, in this case, the StringBuilder class is a subclass of AbstractStringBuilder. This is a choice of the implementing programmer as you won't find this in the docs.

I found the charAt function was implemented on the AbstractStringBuilder base class. Here are some screenshots of the code in question, from my system. (Remember, specific JDK is Oracle & Java 1.8).

enter image description here

enter image description here

Update

I actually ran some code to test what exceptions are thrown and found something interesting. The charAt() function in my environment also throws a java.lang.StringIndexOutOfBoundsException. Here is the code I ran to test it:

public class StringVsStringBuilder {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder("one");
        String string = "two";

        try {
            builder.charAt(10); // Clearly out of bounds
        } catch (IndexOutOfBoundsException e) {
            System.out.println(String.format(
                    "Exception encountered accessing character out of index bounds on variable 'builder': %s",
                    e.getClass()
            ));
        }

        try {
            string.charAt(10); // Clearly out of bounds
        } catch (IndexOutOfBoundsException e) {
            System.out.println(String.format(
                    "Exception encountered accessing character out of index bounds on variable 'string': %s",
                    e.getClass()
            ));
        }
    }
}

And here is the output when run (Windows 10, 64bit, Oracle JDK 1.8.0_191):

Exception encountered accessing character out of index bounds on variable 'builder': class java.lang.StringIndexOutOfBoundsException
Exception encountered accessing character out of index bounds on variable 'string': class java.lang.StringIndexOutOfBoundsException

Process finished with exit code 0

To address some questions from the comments:

  • [AbstractStringBuilder] isn't in the docs which means we can't write programs with it?

    One usually follows the other. The AbstractStringBuilder class is private to the java.lang package, meaning that it cannot be access from outside of that package. This means that you would not be able to import and use this class in your code. This is a perfect example of some portion of code that is not part of the public API of the system (in this case, the JDK itself), in which case, it will not be documented, for several reasons:

    1. You cannot use it
    2. Implementation details could potentially change constantly
    3. It should not affect the use of the public API (the documented portion of the system)
  • if charAt() in StringBuilder throws a StringIndexOutOfBoundsException, then why does the docs say simply IndexOutOfBoundsException?

    This is because that is the described behavior of the charAt() function based on the design intent of the class. Since you can create any number of sub-classes for exceptions (just like any other Java class that is not final) then it is always up to the implementing programmer to choose what it best to return (or throw) from a function, as long as the function adheres to the agreed upon contract (interface, doc spec, abstract class, etc.).

    Having said that, you could implement the JDK right now and choose your own subclass of IndexOutOfBoundsException to throw in this scenario. If I were to use StringBuilder.charAt() in my code, I would catch the exception that is described in the docs, because that would allow me the most flexibility/portability without risking breaks from reliance on implementation details. Entire books have been written on this topic, so I will leave the discussion here: always try to catch the most specific exception that you know could be thrown, meaning, if it is possible that other implementations may not throw StringOutOfBoundsException, then you better be catching just IndexOutOfBoundsException since it is guaranteed to be of that type (or a subclass of it).

Update 2

In this specific case, you can pretty much ignore everything I mentioned above and just focus on the following:

charAt() is an interface method described in java.lang.CharSequence. This means that for any implementing class, the issue raised when an index out of the bounds of the sequence is attempted to be accessed will be an IndexOutOfBoundsException. The interface describes general behavior and it is not necessary for this behavior to be defined in terms of a String, a StringBuilder, an AlphabetSoup or any other specific implementation. If those impls. decide to subclass that exception and give more specific error info in each case, that is fine, but it does not change the general contract. If you are writing a generic character processing system, then you will likely write to the interface and not care about the underlying impl. In which case, you will only know (or care) about IndexOutOfBoundsException and nothing else.

Sign up to request clarification or add additional context in comments.

6 Comments

quick question unrelated slightly, abstractstringbuilder isn't in the docs which means we can't write programs with it?
and do you mean jdk? not jvm
Sorry for the spam, if charAt() in StringBuilder throws a StringIndexOutOfBoundsException, then why does the docs say simply IndexOutOfBoundsException?
I'll try to address your questions in an update to my answer
Yes, I will add this to the answer as well.
|
1

This seems is a document typo.

In Java HotSpot(TM) 10.0.1, String.substring does throw StringIndexOutOfBoundsException:

public String substring(int beginIndex, int endIndex) {
    int length = length();
    checkBoundsBeginEnd(beginIndex, endIndex, length); //here
    int subLen = endIndex - beginIndex;
    if (beginIndex == 0 && endIndex == length) {
        return this;
    }
    return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
                      : StringUTF16.newString(value, beginIndex, subLen);
}


static void checkBoundsBeginEnd(int begin, int end, int length) {
    if (begin < 0 || begin > end || end > length) {
        throw new StringIndexOutOfBoundsException(
            "begin " + begin + ", end " + end + ", length " + length);
    }
}

1 Comment

It is not a typo. The spec describes that it throws an IndexOutOfBoundsException because that is the intent. If a more specific exception is thrown, it is simply because the programmer thought it more useful. That does not change the spec of the method, which means you shouldn't expect all implementations to throw the same specific exception. Furthermore, even if the intent is to throw a StringIndexOutOfBoundsException this would not be a typo since it is an IndexOutOfBoundsException (polymorphism).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.