TL;DR
What are the design decisions behind Matcher‘s API?
Background
Matcher has a behaviour that I didn’t expect and for which I can’t find a good reason. The API documentation says:
Once created, a matcher can be used to perform three different kinds of match operations:
[…]
Each of these methods returns a boolean indicating success or failure. More information about a successful match can be obtained by querying the state of the matcher.
What the API documentation further says is:
The explicit state of a matcher is initially undefined; attempting to query any part of it before a successful match will cause an IllegalStateException to be thrown.
Example
String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
System.out.println(matcher.group("foo")); // (1)
System.out.println(matcher.group("bar"));
This code throws a
java.lang.IllegalStateException: No match found
at (1). To get around this, it is necessary to call matches() or other methods that bring the Matcher into a state that allows group(). The following works:
String s = "foo=23,bar=42";
Pattern p = Pattern.compile("foo=(?<foo>[0-9]*),bar=(?<bar>[0-9]*)");
Matcher matcher = p.matcher(s);
matcher.matches(); // (2)
System.out.println(matcher.group("foo"));
System.out.println(matcher.group("bar"));
Adding the call to matches() at (2) sets the Matcher into the proper state to call group().
Question, probably not constructive
Why is this API designed like this? Why not automatically match when the Matcher is build with Patter.matcher(String)?
Actually, you misunderstood the documentation. Take a 2nd look at the statement you quoted: –
A matcher may throw
IllegalStateExceptionon accessingmatcher.group()if no match was found.So, you need to use following test, to actually initiate the matching process: –
The below code: –
Just creates a
matcherinstance. This will not actually match a string. Even if there was a successful match.So, you need to check the following condition, to check for successful matches: –
And if the condition in the
ifreturnsfalse, that means nothing was matched. So, if you usematcher.group()without checking this condition, you will getIllegalStateExceptionif the match was not found.Suppose, if
Matcherwas designed the way you are saying, then you would have to do anullcheck to check whether a match was found or not, to callmatcher.group(), like this: –The way you think should have been done:-
But, what if, you want to print any further matches, since a pattern can be matched multiple times in a String, for that, there should be a way to tell the matcher to find the next match. But the
nullcheck would not be able to do that. For that you would have to move your matcher forward to match the next String. So, there are various methods defined inMatcherclass to serve the purpose. Thematcher.find()method matches the String till all the matches is found.There are other methods also, that
matchthe string in a different way, that depends on you how you want to match. So its ultimately onMatcherclass to do thematchingagainst the string.Patternclass just creates apatternto match against. If thePattern.matcher()were tomatchthe pattern, then there has to be some way to define various ways tomatch, asmatchingcan be in different ways. So, there comes the need ofMatcherclass.So, the way it actually is: –
So, if there are 4 matches found in the string, your first way, would print only the first one, while the 2nd way will print all the matches, by moving the
matcherforward to match the next pattern.I Hope that makes it clear.
The documentation of
Matcherclass describes the use of the three methods it provides, which says: –Unfortunately, I have not been able find any other official sources, saying explicitly Why and How of this issue.