How Does the Java Cast Operator Work

How does the Java cast operator work?

Is the JLS good enough?

Casting conversion is applied to the operand of a cast operator (§15.16): the type of the operand expression must be converted to the type explicitly named by the cast operator. Casting contexts allow the use of:

  • an identity conversion (§5.1.1)
  • a widening primitive conversion (§5.1.2)
  • a narrowing primitive conversion (§5.1.3)
  • a widening reference conversion (§5.1.5) optionally followed by an unchecked conversion (§5.1.9)
  • a narrowing reference conversion (§5.1.6) optionally followed by an unchecked conversion
  • a boxing conversion (§5.1.7)
  • an unboxing conversion (§5.1.8).

Actually, maybe this part is more relevant:

The detailed rules for compile-time legality of a casting conversion of a value of compile-time reference type S to a compile-time reference type T are as follows:


  • If
    S is a class type:
    • If T is
      a class type, then either |S|
      <: |T|, or |T| <:
      |S|; otherwise a compile-time
      error occurs. Furthermore, if there
      exists a supertype X of
      T, and a supertype Y of
      S, such that both X and
      Y are provably distinct
      parameterized types (§4.5),
      and that the erasures of X and
      Y are the same, a compile-time
      error occurs.
    • If T is an interface type:

      • If
        S is not a final
        class (§8.1.1),
        then, if there exists a supertype
        X of T, and a supertype
        Y of S, such that both
        X and Y are provably
        distinct parameterized types, and that
        the erasures of X and Y
        are the same, a compile-time error
        occurs. Otherwise, the cast is always
        legal at compile time (because even if
        S does not implement T,
        a subclass of S might).
      • If S is a
        final class (§8.1.1),
        then S must implement T,
        or a compile-time error occurs.

    • If T
      is a type variable, then this
      algorithm is applied recursively,
      using the upper bound of T in
      place of T.
    • If T is
      an array type, then S must be
      the class Object, or a
      compile-time error occurs.
  • If S is
    an interface type:
    • If T is
      an array type, then T must
      implement S, or a compile-time
      error occurs.
    • If T is a type that is not
      final (§8.1.1),
      then if there exists a supertype
      X of T, and a supertype
      Y of S, such that both
      X and Y are provably
      distinct parameterized types, and that
      the erasures of X and Y
      are the same, a compile-time error
      occurs. Otherwise, the cast is always
      legal at compile time (because even if
      T does not implement S,
      a subclass of T might).
    • If T is
      a type that is final,
      then:
      • If S is not a parameterized
        type or a raw type, then T must
        implement S, and the cast is
        statically known to be correct, or a
        compile-time error occurs.
      • Otherwise,
        S is either a parameterized
        type that is an invocation of some
        generic type declaration G, or
        a raw type corresponding to a generic
        type declaration G. Then there
        must exist a supertype X of
        T, such that X is an
        invocation of G, or a
        compile-time error occurs.
        Furthermore, if S and X
        are provably distinct parameterized
        types then a compile-time error
        occurs.
  • If S is
    a type variable, then this algorithm
    is applied recursively, using the
    upper bound of S in place of
    S.
  • If
    S is an array type SC[],
    that is, an array of components of
    type SC:
    • If T is a
      class type, then if T is not
      Object, then a
      compile-time error occurs (because
      Object is the only class
      type to which arrays can be assigned).
    • If T
      is an interface type, then a
      compile-time error occurs unless
      T is the type
      java.io.Serializable or
      the type Cloneable, the
      only interfaces implemented by arrays.
    • If T
      is a type variable, then:
      • If the upper
        bound of T is
        Object or the type
        java.io.Serializable or
        the type Cloneable, or a
        type variable that S could
        legally be cast to by recursively
        applying these rules, then the cast is
        legal (though unchecked).
      • If the upper
        bound of T is an array type
        TC[], then a compile-time error
        occurs unless the type SC[] can
        be cast to TC[] by a recursive
        application of these compile-time
        rules for casting.
      • Otherwise, a
        compile-time error occurs.
    • If T is
      an array type TC[], that is, an
      array of components of type TC,
      then a compile-time error occurs
      unless one of the following is true:


    • TC and SC are the
      same primitive type.
    • TC and
      SC are reference types and type
      SC can be cast to TC by
      a recursive application of these
      compile-time rules for casting.

Perfectly clear now, isn't it? :D

In other words, this is the best I can do without knowing more details about your problem.

Java Class.cast() vs. cast operator

I've only ever used Class.cast(Object) to avoid warnings in "generics land". I often see methods doing things like this:

@SuppressWarnings("unchecked")
<T> T doSomething() {
Object o;
// snip
return (T) o;
}

It's often best to replace it by:

<T> T doSomething(Class<T> cls) {
Object o;
// snip
return cls.cast(o);
}

That's the only use case for Class.cast(Object) I've ever come across.

Regarding compiler warnings: I suspect that Class.cast(Object) isn't special to the compiler. It could be optimized when used statically (i.e. Foo.class.cast(o) rather than cls.cast(o)) but I've never seen anybody using it - which makes the effort of building this optimization into the compiler somewhat worthless.

What is point of type casting?

char is an integral type in Java, and when you perform arithmetic the result is an int (JLS-4.2.2. Integer Operations says, in part, the numerical operators, which result in a value of type int or long and adds that does include the additive operators + and -).

char c = 'A';
System.out.printf("'%c' = %d%n", c, (int) c);
int d = (c - 'A' + 'a'); // c - 65 + 97
System.out.printf("'%c' = %d%n", (char) d, d);

And I get

'A' = 65
'a' = 97

What if a cast operator is used in shift operators

The Java 8 JLS also states in §5.6.1:

5.6.1. Unary Numeric Promotion

Some operators apply unary numeric promotion to a single operand, which must produce a value of a numeric type:

...

  • Otherwise, if the operand is of compile-time type byte, short, or char, it is promoted to a value of type int by a widening primitive conversion (§5.1.2).

...

Thus, if we take the following expression:

int i = ...
short s = (short) i << 2;

Will result in a compiler error:

Main.java:4: error: incompatible types: possible lossy conversion from int to short
short s = (short) i << 2;

Ideone demo

This is due to the fact that the cast binds to the first argument of the shift, not the whole expression. Here is the whole expression with explicit parenthesis:

short s = ((byte) i) << 2;

Whereas

int i = ...;
int j = (short) i << 2;

Will successfully compile.

Ideone demo

So the effective bits to use for anything < int is the same as for int (5 bits) since they are upcasted to int automatically.

If you cast the result of the whole expression to, e.g. short (short s = (short) (i << 2), then there is no automatism that takes place in the compiler. But Bohemian's answer gives a logical bound as to what bits of the right hand operator will effectively influence the value after the cast.


In newer JLS versions, the section has been reworded. For example, in Java 14 JLS, §5.6 we find (the section is shortened for breviety, I recommend reading the whole paragraph to get the full context):

5.6. Numeric Contexts

Numeric contexts apply to the operands of arithmetic operators, array creation and access expressions, conditional expressions, and the result expressions of switch expressions.

An expression appears in a numeric arithmetic context if the expression is one of the following:

...

  • An operand of a shift operator <<, >>, or >>> (§15.19). Operands of these shift operators are treated separately rather than as a group. A long shift distance (right operand) does not promote the value being shifted (left operand) to long.

...

Numeric promotion determines the promoted type of all the expressions in a numeric context. The promoted type is chosen such that each expression can be converted to the promoted type, and, in the case of an arithmetic operation, the operation is defined for values of the promoted type. The order of expressions in a numeric context is not significant for numeric promotion. The rules are as follows:

...


  1. Next, widening primitive conversion (§5.1.2) and narrowing primitive conversion (§5.1.3) are applied to some expressions, according to the following rules:

    ...

    • Otherwise, none of the expressions are of type double, float, or long. In this case, the kind of context determines how the promoted type is chosen.

      In a numeric arithmetic context or a numeric array context, the promoted type is int, and any expressions that are not of type int undergo widening primitive conversion to int.

      In a numeric choice context, the following rules apply:

      ...

      • Otherwise, the promoted type is int, and all the expressions that are not of type int undergo widening primitive conversion to int.

      ...

Use of type cast operator within print statement

You can only cast a primitive to another primitive or an object to a type it is an instance of. For example, you can cast a String to an Object and an int into a long. If you want an int in a string, use String.format or concatenation will handle the conversion automatically:

System.out.print(String.format("The value of integer1 is %d", integer1));

or

System.out.print("The value of integer1 is " + integer1);

BTW, An important thing in Java about primitives and casting is that you can't cast boxed primitives to other boxed types. For example if you have

Integer foo = 1000;
Long bar = foo;

You'll get a ClassCastException, but

int foo = 1000;
long bar = foo;

will work fine

To do the same with boxed vesions:

Integer foo = 1000;
Long bar = foo.longValue();

Char casting in java

It's because (num%b2) is an int.

The type of a conditional expression is the common type by which the two operands can be represented. So, if the "true" operand is a char but the "false" operand is an int, the result of someCondition ? someChar : someInt is an int.

It's a lot clearer if you just write it as a plain old if-else statement:

if (num%b2 >= 10) {
result.append((char)('A'+((num%b2)-10)));
} else {
result.append(num%b2);
}


Related Topics



Leave a reply



Submit