Java Type Casting and Numeric Conversion
Convert values deliberately. Follow the order of arithmetic and conversion, discover when information disappears, and build a report whose fractional and whole-number fields mean exactly what you intend.
Before you start: complete Java Data Types. The examples are complete programs in the existing Java 21 runner. Predict the result, run it, then change one value. This lesson focuses on numeric conversions; object downcasting and wrapper conversions belong to later lessons.
What you will be able to do
- Distinguish implicit numeric conversion from an explicit cast.
- Identify information lost through truncation, limited precision, or narrowing.
- Place a cast before the operation that needs a different arithmetic type.
- Explain why byte arithmetic can produce int.
- Separate casting from parsing, rounding, and validation.
- Build and test a progress report with deliberate conversion choices.
Conversion changes a value's representation, not a variable's declared type
A conversion produces a value in another type when Java's rules allow it. It might happen automatically during assignment or arithmetic, or you might request it explicitly with a cast. It does not rewrite the declared type of the original variable. An int count remains int even when you use its value in a double calculation.
A numeric cast has the form (targetType) expression. In (double) totalMinutes, the target type is double and the expression is totalMinutes. The converted value is available to the surrounding expression. The original variable is not changed unless you separately assign something to it.
Think in three questions: what type and value does this expression have now, what operation happens next, and what information could conversion lose? Casting is not a general repair button. A compiling expression may still use the wrong arithmetic or discard information that your report needs.
Widening often happens automatically
Java allows certain widening numeric conversions without an explicit cast. An int can be assigned to long or double, for example. In the first program, the integer 12 is exactly representable in both destination types. The displayed .0 tells you the double is printed in floating-point form; it does not mean a task was split into a fraction.
For ordinary numeric assignments, byte can widen to short, int, long, float, or double; short can widen to int, long, float, or double. char can widen to int and the larger numeric types, but not implicitly to short. int can widen to long, float, or double; long to float or double; and float to double. boolean is not part of these numeric paths.
Do not interpret this as a promise that every widening conversion preserves every digit. Integral widening, such as int to long, preserves the value. Some conversions to floating point do not. Also, the fact that char and short both use 16 bits does not make them interchangeable: char is unsigned while short is signed. Check the actual language conversion, not only the bit count.
Main.java · Convert without changing the original variable
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original int: 12
As long: 12
As double: 12.0
Widening is not a synonym for exact
The precision example converts an int count to float. This is permitted automatically, yet 16,777,217 is not exactly representable as float. The nearby representable value is 16,777,216, so the conversion loses a unit. A larger numeric magnitude range does not imply enough precision to distinguish every integer within it.
Casting the float back to int cannot recover the missing unit. It converts the approximate value that exists now, not the original count. The formatted line uses zero decimal places so the difference is easy to compare without scientific notation. That formatting only changes how the value is displayed.
Keep exact counts in an appropriate integer type unless a calculation genuinely requires a different representation. int to double preserves every int value, but sufficiently large long values can lose precision when converted to double. Try 16,777,216 and then 16,777,218 in this example; both are representable as float, showing why one passing test does not prove that all nearby values survive.
Main.java · Widening can still lose precision
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original: 16777217
As float: 16777216
Back to int: 16777216
Narrowing from double to int is not rounding to nearest
Assigning a double variable directly to int normally fails compilation because information could be lost. The explicit cast (int) positive permits conversion in the program below. For these finite values within int's range, the fractional part is discarded toward zero: 8.9 becomes 8, and -8.9 becomes -8.
This differs from rounding to nearest and from always moving downward on the number line. A negative result of -8 is closer to zero than -8.9, not its mathematical floor of -9. Choose truncation only when discarding the fractional part matches the meaning of your data. For example, reporting whole minutes may deliberately omit remaining seconds.
The original doubles remain unchanged. If your rule instead needs rounding, choose a suitable rounding operation and document its behavior; a cast is not a substitute. For values outside range, infinities, and NaN, Java has additional defined conversion rules. Those rules still do not constitute validation of a measurement or count. Treat this first example as a deliberate in-range truncation.
Main.java · A cast drops a fractional part toward zero
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Positive whole part: 8
Negative whole part: -8
Original positive: 8.9
Narrowing an integer can change its magnitude and sign
A byte holds values from -128 to 127. The value 130 fits int but not byte. Writing (byte) count allows the conversion; it does not make 130 fit. Narrowing to byte retains only the low eight bits and interprets them in byte's signed representation, producing -126 in this example.
Nothing in that cast asks Java to throw an exception, clamp the count at 127, or reject the input. If the original count must be preserved, choose an appropriate type or check its range before narrowing. The absence of a compiler error is not evidence that a conversion met your program's requirements.
Try count values 127 and 128. The former stays 127, while the latter becomes -128. Then try -129, which becomes 127. These boundary experiments explain more than testing a comfortable value such as 10. In normal report code, retaining int is often clearer than forcing a count into byte without a concrete storage or API requirement.
Main.java · An integer cast is not a range check
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original count: 130
After byte cast: -126
Check the byte-boundary predictions
127 remains 127, 128 becomes -128, and -129 becomes 127. These results follow the narrowing representation, not a business rule for valid counts. Keeping int would avoid this unnecessary loss when no byte representation is required.
Cast before the operation that needs a different type
We want the average of 17 minutes across four sessions. In totalMinutes / sessions, both operands are int, so Java performs integer division first and produces 4. Assigning that result to double gives 4.0. An explicit cast wrapped around that completed division still gives 4.0: the lost fractional part is already gone.
In (double) totalMinutes / sessions, the cast applies to the numerator before division. The division therefore operates on double values and gives 4.25. You only need one operand converted before this numeric operation; Java promotes the other operand as required. Casting the denominator instead would also work for this calculation.
Read parentheses carefully. (double) (a / b) converts the answer; (double) a / b converts an operand. Neither expression validates b. If an integer denominator is zero, integer division throws ArithmeticException; floating-point division follows different rules and can produce infinity or NaN. This report assumes a positive session count, which a real input workflow must enforce.
Main.java · Cast an operand, not an already truncated answer
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Assigned after division: 4.0
Cast after division: 4.0
Cast before division: 4.25
Trace the three average expressions
- Integer division produces 4, which assignment then converts to 4.0.
- Integer division produces 4, which the outer cast then converts to 4.0.
- The numerator becomes 17.0 first; double division by four produces 4.25.
Now change totalMinutes to 19. The first two results remain 4.0; the third becomes 4.75.
Small integer arithmetic is promoted to int
With byte firstGroup and byte secondGroup, the addition firstGroup + secondGroup has type int. Java promotes these small integer operands for ordinary arithmetic. That is why assigning their sum directly to byte can fail even when the particular values 10 and 20 make a total that would fit.
The example keeps the sum as int, then explicitly converts it to byte to demonstrate the syntax. This cast happens to preserve 30, but it would not protect a larger total. Change both groups to 100: the int sum is 200, while the byte result becomes -56. A cast resolves the type mismatch without proving the data is safe to narrow.
You may also see byte small = 30; compile without a cast. Assignment permits certain compile-time constant expressions that fit the target type. A sum of ordinary non-final local variables is a different case. Compound assignment can include an implicit narrowing conversion too; do not use += just to hide a type error. Prefer a clear int result unless a narrower representation is truly required.
Main.java · Small integer operands still produce int
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Combined int: 30
Explicit byte result: 30
Check the larger-group prediction
With both groups set to 100, combined is 200 and compactCombined is -56. The explicit cast makes the conversion permitted; it does not make the total fit byte.
Casting after overflow cannot repair the earlier result
Two groups-of-items counts of 50,000 give a mathematical product of 2,500,000,000, beyond positive int range. In (long) (groupCount * itemsPerGroup), the multiplication still happens as int first and overflows. The cast then converts the already incorrect negative int to long without repairing it.
In (long) groupCount * itemsPerGroup, the first operand is converted before multiplication, so the operation uses long arithmetic. The intended result fits long and is preserved. Parentheses do not provide extra precision by themselves; they determine which expression is converted and when.
This is the same reasoning as the average example: inspect the arithmetic operation before inspecting the destination variable. long is not unlimited, so range analysis still matters. For fixed literal operands, a long suffix can also establish long arithmetic. For variable operands, an early numeric cast is one way to make the intended calculation type explicit.
Main.java · Widen before multiplication can overflow
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Cast after multiplication: -1794967296
Cast before multiplication: 2500000000
Casting, parsing, and interpreting a character are different tasks
The String "42" contains text, not an int. A numeric cast does not read those digits. Integer.parseInt(countText) asks a library method to interpret suitable whole-number text and return an int. It can throw NumberFormatException when the text is invalid or outside int range. Parsing is its own operation, not a widening or narrowing cast.
Similarly, casting the char '7' to int produces its UTF-16 code-unit value, 55, not the quantity seven. Parsing the String "7" produces 7. The values can look related to a human while representing different information to Java. Use the operation that matches the meaning you need instead of experimenting with casts until code compiles.
This brief example introduces parseInt only to draw that boundary. We will handle user input and rejected values in the input lesson. Numeric casts also do not convert int to boolean, and they are not how you format numbers for display. Keep conversion, parsing, presentation, and business validation as separate questions.
Main.java · Parse numeric text; do not cast it
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Parsed count plus one: 43
Code unit for digit: 55
Parsed digit: 7
Use checked conversion when silent narrowing is unacceptable
Math.toIntExact takes a long and returns an int only when the value fits int. Otherwise it throws ArithmeticException instead of silently narrowing. In the example, 120 fits and prints unchanged. Change importedCount to 3_000_000_000L to observe an explicit failure rather than a misleading wrapped count.
This checks a representation boundary, not every business rule. A value of -1 fits int, so toIntExact accepts it even if a negative imported count is invalid. Likewise, a plain cast does not check that a floating-point value is finite, nonnegative, integral, or suitable for your application. Define those requirements separately.
You have not learned exception handling or conditions yet, so this example deliberately allows the checked conversion to stop the program when the value is too large. Later lessons will handle rejected input intentionally. For now, recognize the difference between a conversion that silently changes a value and an operation that reports a range failure.
Main.java · Require a long value to fit int
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Checked int count: 120
Repair two conversions that happen too late
This program compiles but reports an average of 4.0 and a negative item total. The intended results are Average: 4.25 and Items: 2500000000. Repair the placement of the casts without changing the input values or hard-coding the answers.
Main.java · Repair the report 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
The original casts convert results after integer division or multiplication. Move each cast to an operand before its arithmetic operation. The average then uses double division, and the total uses long multiplication. Both original expressions already compiled, so reading compiler diagnostics alone could not reveal these wrong-result bugs.
Main.java · Repaired calculations
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Average: 4.25
Items: 2500000000
Independent lab: a deliberate progress report
Build a study-progress report that uses a decimal average and percentage while deliberately reporting whole study minutes by truncation. These are fixed local example values, not saved course progress. Explain each conversion before you compare with the solution.
- Declare int completedLessons as 7 and int plannedLessons as 16. Assume plannedLessons is positive for this exercise.
- Calculate double completionPercent by converting completedLessons before division, then multiplying the ratio by 100.
- Declare int totalPracticeMinutes as 17 and int sessions as 4, and calculate a double averageMinutes without losing the fractional part.
- Declare double trackedMinutes as 98.75. Use an explicit cast to create int wholeMinutes, deliberately discarding the fraction toward zero.
- Print completion percent and the average with two decimal places using Locale.ROOT, plus the original tracked minutes and whole minutes as shown.
- Keep all source values unchanged, do not hard-code calculated answers, and test the alternative inputs below.
Use this output contract to check both the calculations and their display:
Completion: 43.75%
Average (minutes): 4.25
Tracked minutes: 98.75
Whole minutes: 98
Main.java · Build your progress report
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Show hints without revealing the solution
Convert a numerator to double before each division. Calculate the completion ratio before multiplying by 100. Keep the original trackedMinutes and store its intentionally truncated value in a separate int. Use %.2f for two decimal places and %% for a printed percent sign.
Reveal the explained lab solution
Main.java · Progress report solution
Predict the result, edit Main.java, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
The first casts happen before division, preserving the fractional average and completion ratio. Multiplying the completed ratio by 100 gives the percentage. The last cast intentionally discards a fraction, so 98.75 becomes 98 rather than 99. printf controls the display only; it does not convert the underlying calculations into exact decimal arithmetic or validate the input assumptions.
Test changes and boundaries
Reset between experiments and predict every affected line. A successful run does not automatically mean the input made sense.
- Set completedLessons to 1: completion should be 6.25%, not 0.00%.
- Set completedLessons to 16: completion should be 100.00%. Set it to 0: completion should be 0.00%.
- Set totalPracticeMinutes to 19: average should be 4.75, not 4.00.
- Set trackedMinutes to 0.75: whole minutes should be 0. Set it to -0.75: whole minutes is also 0, demonstrating truncation rather than validation.
- Set plannedLessons to 0: floating-point division produces Infinity here. Do not accept that as a valid progress percentage; a real report must reject a nonpositive denominator.
Common mistakes and how to diagnose them
First decide whether you have an incompatible assignment, an arithmetic result that lost information, a parsing failure, or an invalid input assumption. Adding the same cast to each situation cannot solve all four.
possible lossy conversion
Java requires you to confront a narrowing assignment. Decide whether a wider destination is the correct design. If narrowing is genuinely intended, specify the cast and check the value and acceptable loss instead of merely silencing the compiler.
The cast compiles but the answer is already wrong
Inspect whether the cast applies before or after the arithmetic. Converting the result of integer division cannot restore its fraction, and widening an overflowed product cannot reconstruct its original mathematical value.
A negative fraction goes the unexpected direction
For finite in-range double-to-int conversion, truncation moves toward zero. -8.9 becomes -8, not -9. If you need floor or nearest-integer rounding, that is a different rule and needs a suitable operation.
String cannot be converted to int
Casting does not interpret numeric text. Use an appropriate parsing method for the expected format, then handle invalid text and range errors intentionally. A char-to-int cast returns a code-unit value, not a parsed digit.
ArithmeticException from toIntExact
The long does not fit int. The checked conversion is reporting a real representation limit. Keep the wider type or handle the rejected value; replacing it with an unchecked cast would discard information.
An infinite or otherwise invalid percentage appears
A floating-point operation can return Infinity or NaN rather than throwing the exception you expected from integer division. Validate denominator and domain assumptions explicitly. A cast is not evidence that a value is finite or meaningful.
Frequently asked questions
Does a cast change the original variable?
No. A numeric cast produces a converted value for an expression. The original variable retains its declared type and stored value unless a separate assignment changes it.
Is widening always lossless?
No. int to long preserves the value, but int to float and sufficiently large long to float or double can lose precision. Widening describes a language conversion category, not a guarantee that every digit survives.
Why can byte value = 10 compile but byte result = a + b fail?
Assignment allows certain constant expressions whose values fit byte. Ordinary byte-variable arithmetic is promoted to int. Keeping the result as int is often clearer than inserting a narrowing cast without a range justification.
What happens when a double is outside int range?
For a direct double-to-int cast, NaN becomes 0, and values beyond the representable integer range, including infinities, convert to the appropriate int limit. Finite in-range values truncate toward zero. These defined results are not an application-level validation rule.
Can I cast numeric text or numbers to boolean?
A numeric cast is not text parsing and does not convert numbers to boolean. Parse supported numeric text with a library method. Define a comparison or other explicit rule when you need a yes/no result; that is covered in the operators lesson.
Ask what happens before the conversion
Widening can be implicit, narrowing may require an explicit cast, and neither label proves every value is preserved. Convert an operand before a calculation when that calculation needs different arithmetic. Treat parsing, rounding, and validation as separate operations.
Before moving on, explain the difference between casting an operand and casting an answer. Then explain why both byte narrowing and whole-minute truncation lose information, yet only one was an intentional report requirement. Next is Java Operators and Booleans.
Can you predict what survives a conversion?
Answer all six questions and read the reasoning. Results are temporary on this page and do not save course progress.
References and related learning
The reporting examples, explanations, and exercises are original SovranCode material. These official references support the language and library rules.
- Java 21 specification: widening and narrowing primitive conversions
- Java 21 specification: cast expressions
- Java 21 Math API: toIntExact
- Java 21 Integer API: parseInt
Review data types · Review variables and constants · Java course roadmap.
Last updated: