Haven't seen a need for it in getters but I suppose a contrived example might be if the getter were to do some sort of I/O (which is probably not a good idea to begin with).
This is my most complicated class. I can't think of anything that obvious that would cause an error.
In my test program I set the input periods to -1. This caused a NegativeArraySizeException so I could catch that.
How would I catch a general error where i don't know the exception type in advance?
Also I would like to throw an exception if periods < 3 or greater than 120.
How would I do that?
Once you have read the tutorials section, you need to consider the concept of an invariant. This is something which must be maintained always. That's not quite true; a method may breach its object's invariants as long as it restores them before it completes. For a grade for a student (a common exercise), there are certain invariants, e.g. number of grades ≥ 0, ∀ grade • grade ≥ 0 ∧ grade ≤ 100. Now you might have an enterGrade method or setGrade.So you try passing -1, -1 to that method and you get an exception. Even better, you pass 1, 999999999 and have now recorded an incorrect grade.
You can consider changing the method to prevent that exception, possibly by having an if statement which does nothing for arguments outwith the permissible ranges, but that can cause confusion to the user.I'll let you work out whether the predicate in line 3 is correct and my error message in line 9 is helpful or not.
Your next line of defence is to supply documentation comments explaining how the method is supposed to work:-The character \u2019 is a posh apostrophe: ’ The characters ≥ and > are coded in the documentation comments by ≥ and >
You still get the exception if you pass -1, -1, but now you can point to the documentation comments and tell off the user that it is their fault for not reading the instructions.
You can now move up one step in the arms race and move into the realms of assertive programming:-Those are unchecked exceptions, and that gives the user several options.
Note that I used the non‑specific RuntimeException; I could have declared IndexOutOfBoundsException and IllegalArgumentException separately. Don't declare ArrayIndexOutOfBoundsException, otherwise you may be committing yourself to implement the object with an array for ever. Note I could have used different kinds of exception in the documentation comment, but the important thing is that the user now knows what input the method takes, and what sort of exception to expect for wrong input. Now the user can decide whether to catch the exception or not, as I explained with the numbered points.
However, it's weird that your client would get a NegativeArraySizeException when they're not working with arrays. So you could check the parameters to throw an unchecked exception that's appropriate for the situation:
However, if you want to support different ranges (such as 3 - 12), just edit the condition that causes the exception to be thrown. You can do the same with the date, employees and salary to check that they're not null when they get passed to your constructor. That way you can enforce that your fields will never hold null values, which is a great worry off your mind.
A few remarks about your code:
Don't use so many arrays. As you've noticed, arrays have a fixed length that needs to be known before you start processing elements. It's much better to use types from the Collections API. Lists and maps can grow dynamically.
Give method parameters descriptive names. The client doesn't know what names such as p, d, e and s mean, so write those identifiers out.
Don't ask for more than you need. The constructor of your class only needs a number of employees, so just pass an integer and not an Employees object.
The client of your class needs to make sure that the employee indices they pass to your getters are consistent with the order they passed the employees into your process method. That's very brittle, and just begs for trouble in the future. Instead, give each employee a unique ID, and look them up by their ID. Maps are great for this purpose. Both the Employees class and the Salary class seem to do nothing more than maps, so just have your class accept maps instead.
Your headCount field isn't actually a count of anything. It seems more like a strangely typed presence list. you should use a Map<X, Set<Y>> instead, where X is whatever type you use to identify a time period, and Y is whatever type you use to identify an employee. You can then get the presence of an employee during a certain period with precense.get(periodId).contains(employeeId).
"Process" is one of the vaguest terms you can use for method names. Every method processes something. Instead, you could name the method something like generateYearlyFinancialReport().
In general, you want to make classes immutable. Try not to change the value of fields after you've constructed an object. It appears you want to make a summary of some statistics, so create a class that holds those statistics and doesn't change them after an instance is created.
Now for an example of using maps and enforcing invariants by throwing exceptions:
Now, you try to implement the MonthlyFinances inner class.
Stephan van Hulst wrote:You shouldn't catch unchecked exceptions. When they arise, it means there is a bug in the code and you need to fix it. For instance, if your constructor gets passed a negative amount of periods, that means the client of the module doesn't adhere to the rules of your class, so simply let them crash and burn. In general, you only catch checked exceptions (like IOException) because it's not a programmer's fault when they happen.
That only works if you are both in an environment where checked exceptions are not considered a Bad Thing, and are not using a framework where that view is held.
For example all exceptions from Hibernate are RuntimeExceptions, similarly with Spring.
I would say that usually applies, but there are exceptions to that rule of thumb. As I said, if the values are in the code or a file, it will be impossible to correct the error while the program is running. But, if you have keyboard input, you might wish to catch the exception in a loop, until you get a correct input:-And that looks all right . . . at least until you realise there is a much better way: avoid that exception with a different sort of loop:-Of course the version with the while loop would probably have faster execution were it not for the fact that keyboard input is something like 1,000,000× slower than program execution.
Stephan van Hulst wrote:You shouldn't catch unchecked exceptions. When they arise, it means there is a bug in the code and you need to fix it. . . .
And now Stephan and I are in agreement that we aren't catching that unchecked exception.
The Files#newBufferedReader() method declares IOException, but the subsequent lines() method doesn't declare such an exception at all. The documentation comments tell the whole story.I don't think I have ever suffered an IOException, but the above code is not immune from crashing because of an IO error causing an exception to be thrown.
Dave Tolls wrote:. . . For example all exceptions from Hibernate are RuntimeExceptions, similarly with Spring.
Dave Tolls wrote:That only works if you are both in an environment where checked exceptions are not considered a Bad Thing, and are not using a framework where that view is held.
Sure, I guess I'm referring to unchecked exceptions from the standard API, and in self-written code. I treat unchecked exceptions from frameworks you mentioned as if they were checked exceptions, and wrap them in checked exceptions if it's appropriate to propagate them.
Campbell Ritchie wrote:But, if you have keyboard input, you might wish to catch the exception in a loop, until you get a correct input
Like you pointed out in your second example, you can do this without catching exceptions.
I have a class I use for console input:
The version I have shown would not be thread‑safe. Obviously the loops with prompts are only suitable for input from the keyboard.
OP: If you have input in GUIs with multiple text boxes or similar, you would have to verify the values of each text box whenever the submit button is pressed; that is a process similar to what we have been discussing, only it has to be done repeatedly. Also please find the logic error in my first post on this thread.
Yes, and no.
Steven Greenbaum wrote:. . . Wouldn’t an integer be of the wrong type?
A correct input is one which is the correct type and correct value, so it might be an integer between 1000 and 2000. There are two parts to that, type and value. But as Stephan told you, in a strongly‑typed language, the compilation process verifies the types and obviates the risk of your accidentally having the wrong type.
It may be possible intentionally to pass the wrong type, but that is a different story.
Campbell Ritchie wrote:That allows me to take advantage of Scanner's ability to read multiple ints in one line.
Yes, and that's exactly what I'm trying to avoid with my method. I want to ask for one value per line, and re-ask the question if the value is not valid. If there is a second question, I don't want to use values that the user entered before the question was posed.
If I wanted to ask the user to enter multiple values in one go, I'd use a method closer to yours.
I am working with a group of programmers in a foreign country. I sent them my project before adding the error checking. They have implemented it themselves and I have asked them to send it back when done because there will be may more use cases to add.
I will ruminate on the many comments you and others have provided and proceed from there.