Java Methods Challenge: A Price Calculator
Turn a written policy into small, testable methods. Build a fictional cart calculator that validates input, calculates in cents, applies rules in the right order, and prints a receipt without hidden state.
Before you start: review method contracts and returned values, read-only versus mutating helpers, local working state, and array traversal. All source examples use Java 21. This lesson has fictional prices and rules; it performs no checkout, tax calculation, inventory update, or payment.
What you will be able to do
- Turn a written pricing policy into small named method contracts.
- Validate related arrays and numeric limits before calculation.
- Use long arithmetic and integer cents with an explicit rounding rule.
- Apply the discount before deciding shipping eligibility.
- Keep calculations independent, read-only, and separate from receipt output.
- Verify normal, empty, invalid, threshold, and maximum-bound cases.
Start with rules that two implementations can agree on
This challenge is a fictional console price calculator. It does not charge a card, reserve stock, calculate real taxes, or use SovranCode's product catalog. The purpose is to combine the methods, arrays, validation, conversions, and scope lessons into one understandable program.
The policy is precise: multiply each unit price by its quantity, add line totals, apply one whole-percent discount to the entire subtotal, round the discount down to whole cents, and subtract it. Then shipping is zero for a zero merchandise amount or an after-discount amount of at least 5000 cents; otherwise it is 300 cents. The final amount is discounted merchandise plus shipping.
Input uses at most 100 paired lines, unit prices from 0 through 1,000,000 cents, quantities from 0 through 1000, and a discount from 0 through 100 percent. Empty carts and zero quantities are valid. These are exercise limits and policy choices, not universal commerce rules. Write them down before coding so boundary tests have a definite expected answer.
Keep values in cents and give units meaningful names
A unit price of 1299 means 1299 cents, displayed later as $12.99. Two units therefore contribute 2598 cents. Keeping this fixed-two-decimal exercise in integer minor units avoids using a floating-point approximation as the working price representation.
lineTotalCents has one calculation and returns its result without printing. The cast to long happens before multiplication. A long return type alone would not make an overflowing int multiplication safe if both operands were still ints during that multiplication.
The stated unit and quantity limits make one line at most 1,000,000,000 cents. This one-line maximum already fits int, but many lines together need more range. Use the long return consistently so the subtotal and discount pipeline does not accidentally narrow that larger amount later.
Main.java · One line has a price and a quantity
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Line cents: 2598
Validate the relationship between arrays, not just each number
The price at index i pairs with the quantity at index i. If the two arrays have different lengths, that relationship is incomplete and a loop could read beyond one array. Check null references and matching lengths before any corresponding element access.
isValidCart also checks the line budget, discount range, and every price/quantity pair. It answers a boolean question without editing the arrays. main will return an invalid-cart message before invoking calculation helpers when that question is false. Those helpers can then rely on an explicitly stated precondition.
A valid empty cart has two existing arrays of length zero. It is different from null or mismatched input. Negative values are rejected, not silently skipped: dropping a bad cart line would create a plausible receipt for data the caller never actually submitted as a valid order.
Main.java · Reject inconsistent inputs before indexing
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Normal cart: true
Mismatched arrays: false
Invalid discount: false
Empty cart: true
One helper handles a line; another handles the collection
The sample has two units at 1299 cents and one unit at 2499 cents. The line totals are 2598 and 2499, so their subtotal is 5097. A paired indexed loop is appropriate because the same position selects both the unit price and its quantity.
subtotalCents starts a local long total at zero and adds each lineTotalCents result. It does not print, alter quantities, apply discounts, or decide shipping. Keeping its contract that narrow makes it possible to test a wrong subtotal without investigating unrelated formatting or shipping logic.
At the maximum validated inputs, 100 lines each contribute at most 1,000,000,000 cents, giving a subtotal no larger than 100,000,000,000 cents. That bound also makes the later multiplication by a percent at most 10,000,000,000,000, within long range. Integer cents still need range reasoning; they are not automatically immune to overflow.
Main.java · Add paired cart lines
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Subtotal cents: 5097
Calculate the sample subtotal by hand
- The first line is 1299 cents per unit times two units: 2598 cents.
- The second line is 2499 cents per unit times one unit: 2499 cents.
- The cart subtotal is 2598 + 2499 = 5097 cents.
No discount or shipping belongs in this subtotal helper. Its return value still represents whole cents, not a display string.
Make the discount's rounding rule part of its contract
Ten percent of 5097 cents is 509.7 cents. This exercise deliberately rounds that discount down to 509 whole cents, leaving 4588 cents of merchandise. A nearest-cent policy would give a different result, so rounding cannot be an unstated implementation detail.
For validated nonnegative operands, subtotal * percent / 100 performs a long multiplication followed by integer division. Truncation toward zero is rounding down in this nonnegative domain. Dividing percent by 100 first would turn most percentages into zero and lose the discount entirely.
Apply this rule once to the whole subtotal, not independently to each line. Rounding separate line discounts can produce a different total. Percent zero must leave the subtotal unchanged; percent 100 must discount all of it. None of these rules are a claim about the rounding policy used by a real shop or payment provider.
Main.java · Apply one explicit rounding policy
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Discount cents: 509
After discount cents: 4588
Why does rounding once per cart matter?
For this sample, rounding the first line's 10-percent discount down gives 259 cents; the second gives 249 cents. Adding those would give 508, not the specified cart-level 509. The lesson's rule deliberately discounts the complete subtotal once.
Use the discounted merchandise amount to decide shipping
shippingCents receives the merchandise amount after discount, not the original subtotal. The boundary is inclusive: 5000 cents qualifies for zero shipping, while 4999 cents incurs 300. Zero merchandise also has zero shipping under this challenge's explicit policy.
In the main sample, the original 5097 cents would pass the threshold, but the discounted 4588 does not. Passing subtotal instead of net would therefore produce an incorrect free-shipping receipt even if all the discount arithmetic were correct.
Keep the zero-amount rule visible instead of letting an empty cart fall into the ordinary under-threshold branch. A fully discounted nonempty cart also has zero merchandise and therefore zero shipping here. Test those cases directly rather than assuming empty and 100-percent-discount examples will behave correctly by accident.
Main.java · Test both sides of the shipping boundary
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
At 4999: 300
At 5000: 0
At zero: 0
Format only after calculations are complete
formatCents splits a nonnegative amount into whole units with division by 100 and fractional cents with remainder by 100. The fractional portion needs a leading zero when it is below ten; otherwise five cents would incorrectly display as $0.5 rather than $0.05.
The helper returns text instead of printing. Calculation helpers continue returning numbers that main can subtract, compare, and add. Do not format an amount into a String and then try to use that display text as the input to the next arithmetic step.
This formatter deliberately uses a dollar symbol, two decimal places, and no grouping separators. It is not a universal currency formatter, and currencies and locale requirements can differ. For this bounded exercise, integer formatting makes every expected receipt line deterministic without an extra floating-point conversion.
Main.java · Format cents without floating-point arithmetic
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
$0.00
$0.05
$1.05
$50.97
Trace five cents without a double
5 / 100 gives the whole-unit portion 0, and 5 % 100 gives the fraction 5. Since that fraction is below ten, the formatter inserts a zero before it. The result is $0.05. Formatting does not round or recalculate the price.
Let main show the sequence without owning every formula
The composition example begins with a validated subtotal of 6000, calculates a 600-cent discount, and obtains 5400 cents of merchandise. Shipping is then zero, so the final amount is also 5400. Each returned value becomes an input to the next meaningful operation.
main is the coordinator: validate, calculate subtotal, calculate discount, subtract discount, choose shipping, add shipping, format, print. It should be easy to read that order without opening every helper. The helpers still own their small formulas and state their required input domains.
Do not add recursion or overloading merely because the preceding lessons covered them. This linear cart traversal is clear as a loop, and the operations have distinct useful names. A challenge demonstrates judgment about which tools fit, not a requirement to use every syntax feature in one program.
Main.java · Follow the pipeline in the policy's order
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Net cents: 5400
Shipping cents: 0
Total cents: 5400
Independent calls and unchanged inputs are part of correctness
A subtotal belongs to one calculation, so its accumulator is local to that invocation. Calling another cart in between two calls for the first must not change either answer. The repeat example gives 5097, then 500, then 5097 again, and zero for an empty cart.
All price-calculation methods read arrays without assigning to their elements. The final lab saves snapshots to verify that the source prices and quantities remain unchanged. A method returning a plausible total while reducing quantities or changing prices would violate the read-only contract.
Avoid hidden static fields for subtotal, discount, or shipping. They make one receipt depend on earlier calls and force callers to remember cleanup. Returning numeric results and keeping local report variables makes the sequence testable without shared working state.
Main.java · Each order calculation starts fresh
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
First: 5097
Other cart: 500
First again: 5097
Empty: 0
Turn each rule into a test before revealing the solution
First compute one normal receipt by hand. Then test empty arrays, zero quantity, zero price, no discount, full discount, and the smallest nonzero amount. Test invalid references, mismatched arrays, negative values, and each maximum plus one separately from accepted input.
For shipping, use after-discount amounts just below, exactly at, and just above 5000. Include a subtotal that crosses below the threshold after discount so a wrong argument cannot hide. For rounding, use an amount that produces a fractional cent discount rather than only multiples of 100.
Finally check identities: discount must be between zero and subtotal, net must equal subtotal minus discount, and total must equal net plus shipping. Repeat calls in different orders and compare input snapshots. These checks locate a broken contract more effectively than one final output assertion alone.
Repair exercise: do not lose the percentage before multiplying
This helper divides the percentage before multiplying, so a 10-percent discount on 5097 cents becomes zero. Repair the expression while preserving the rule that the final discount is rounded down to whole cents.
Main.java · Repair discount arithmetic
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Reveal the explained repair
With int operands, 10 / 100 is zero before the multiplication begins. Multiply the long subtotal by percent first, then divide by 100 once. The validated range keeps that intermediate product within long. The corrected 509-cent result follows the stated round-down policy; changing to floating point or rounding each line separately would be a different implementation contract.
Main.java · Repaired discount calculation
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Discount cents: 509
Independent challenge: assemble the complete calculator
Build the complete fictional receipt from paired price and quantity arrays. Use calculation-only helpers with clear preconditions, validate before indexing, and keep the orchestration and output in main. This program is not connected to Stripe, a product catalog, inventory, or actual purchases.
- Implement isValidCart(int[] unitCents, int[] quantities, int discountPercent) with the stated limits: non-null equal-length arrays, at most 100 lines, prices 0..1_000_000, quantities 0..1000, and percent 0..100. Empty arrays are valid.
- Implement long-returning lineTotalCents and subtotalCents. Use a local long accumulator and widen before multiplication. Calculation helpers require a validated cart and must not mutate arrays or print.
- Implement discountCents(long subtotal, int percent) as a once-per-cart whole-percent discount rounded down. Compute net as subtotal minus discount; do not apply the percent to shipping.
- Implement shippingCents(long netCents): zero when net is zero or at least 5000, otherwise 300. Implement formatCents(long cents) for the nonnegative two-decimal dollar display.
- In main, on invalid input print Invalid cart: check arrays, line limits, prices, quantities, and discount. and return without a receipt. For accepted input save snapshots, calculate in the defined order, and print Subtotal:, Discount:, After discount:, Shipping:, and Total:.
- Finish with Input unchanged: and a comparison of both input arrays with their snapshots. Verify repeated calls, empty carts, percentage endpoints, threshold transitions, fractional-cent rounding, and maximum validated amounts.
The starter supplies unit prices 1299 and 2499 cents, quantities 2 and 1, and discount 10 percent. Edit the source data; no standard input is needed. Expected output:
Subtotal: $50.97
Discount: $5.09
After discount: $45.88
Shipping: $3.00
Total: $48.88
Input unchanged: true
Main.java · Build your price calculator
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Stage 1 hints: contracts and validation
Write the validator first. Guard null before length, then matching lengths, the 100-line budget, percent bounds, and every price/quantity pair. An invalid line rejects the whole cart; do not silently continue past it. Return before taking snapshots or calculating on rejected data.
Stage 2 hints: calculation and orchestration
Use lineTotalCents inside subtotalCents and initialize the subtotal locally. Multiply before dividing for the discount. Store net in a clearly named variable and pass that to shippingCents. Only then compute the final total and call formatCents for the printed receipt.
A loop is sufficient for these paired arrays. No extra class, recursive call, mutable field, or overload is needed to complete the challenge.
Reveal the explained complete solution
Main.java · Complete fictional receipt calculator
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Two units at 1299 contribute 2598, then the 2499 line produces subtotal 5097. The one cart-level discount is 509, leaving 4588. That is below the after-discount threshold, so shipping is 300 and the final amount is 4888 cents. The receipt formatter changes only representation; arithmetic stays in long cents. Validation happens before snapshots or paired indexing, and every helper call operates without modifying input. Empty or fully discounted merchandise has net zero and therefore no shipping under this exercise's policy.
Test changes and boundaries
Before running each variant, write the expected amounts in cents. Reset restores the original program. Check the individual receipt lines and the input comparison, not just the final total.
- Run the supplied order and confirm all six output lines, especially the 509-cent discount and 300-cent shipping. Repeating calculations must give identical values without altering input.
- Try two empty arrays, then zero quantity and zero price cases. Expect zero merchandise and zero shipping. Try percent 100 on a nonempty cart: net, shipping, and total must all be zero.
- With percent 0, try a single line at 4999, 5000, and 5001 cents. The first costs 300 shipping; the latter two do not. Try 5000 with a 1-percent discount to cross below the threshold.
- Reject null, mismatched lengths, 101 lines, negative values, prices above 1_000_000, quantities above 1000, and a percent outside 0..100. Rejected data must print only the invalid-cart message.
- Test 100 lines at price 1_000_000 and quantity 1000. The subtotal must be 100_000_000_000 cents without int overflow. Test discounts 0, 1, and 100, and verify discount <= subtotal and total == net + shipping.
Common mistakes and how to diagnose them
A nonzero percentage produces zero discount
The percent may have been divided by 100 using integer arithmetic before multiplication. Multiply the validated long subtotal first, then divide once to implement the chosen round-down rule.
A discounted order receives the wrong shipping fee
Check the argument to shippingCents: the contract needs net merchandise after discount. Test a subtotal just at or above the threshold that becomes lower after discount.
An empty cart is charged shipping
The zero-net rule may be missing. Under this fictional policy, zero merchandise has zero shipping. Make that rule explicit before the ordinary positive-under-threshold branch.
Paired indexing fails or a bad line is silently omitted
Validate both references, equal lengths, and all values before calculating. Invalid input should reject this cart, not be silently skipped into a different receipt.
Large totals overflow despite a long result variable
Check the type used during each operation and the accumulator's type, not just its final destination. Widen before multiplication and retain the validated line-count and numeric bounds through the discount intermediate.
The receipt has five cents displayed as $0.5
The remainder must be padded to two decimal digits. Keep formatting separate from arithmetic and test 0, 1, 5, 10, and 105 cents rather than only whole-dollar amounts.
Frequently asked questions
Is this real checkout logic?
No. This is a local teaching calculator with fictional policy choices. It does not collect payment, apply real tax rules, change stock, or connect to SovranCode products.
Why use long cents rather than double prices here?
The exercise has two-decimal minor units and an explicit integer rounding policy. long preserves those whole units and provides room for totals beyond int range. It still needs validated bounds, especially before multiplying by a percent.
Does every calculation helper need to validate everything again?
These helpers state a validated-input precondition, and main enforces it before calling them. Other callers must honor the same contract. A different public API could choose to validate internally; do not leave the assumption undocumented.
Why not use recursion because it was the previous lesson?
A simple paired traversal is clearer as a loop and needs no growing recursive call stack. The challenge is choosing appropriate tools and contracts, not using every recent syntax feature.
What should an empty cart return?
With two valid empty arrays, subtotal, discount, net, shipping, and total are all zero. Null or mismatched arrays are rejected instead of being silently treated as empty.
Clear contracts make a larger program easier to trust
The calculator combines validation, paired traversal, numeric range reasoning, returned values, and local state. Each helper has one small purpose; main makes their order visible. A receipt is correct only when the individual formulas and the values passed between them follow the same written policy.
Before moving on, explain why the sample pays shipping after discount, why invalid data is rejected rather than skipped, and why integer cents still need bounds. Next: OOP, classes, and objects, where you will begin organizing related data and behavior into models.
Can you defend every line of the receipt?
Answer all six questions and read the explanations. Results are temporary on this page and do not save course progress.
References and related learning
The policy, scenarios, explanations, examples, and exercises are original SovranCode teaching material. Official references support the Java 21 arithmetic and method rules, not the fictional commerce policy.
- Java 21 specification: integer division
- Java 21 specification: numeric contexts and promotion
- Java 21 specification: method declarations
Review returned results · Review numeric conversions · Review independent local state · Java course roadmap.
Last updated: