13

What is the difference between compile time and run time type of any object in Java ? I am reading Effective Java book and Joshua Bloch mentions about compile time type and run time types of array instances in Item 26 many times mainly to describe that suppressing cast warnings can be safe sometimes.

// Appropriate suppression of unchecked warning
public E pop() {
 if (size == 0)
   throw new EmptyStackException();
   // push requires elements to be of type E, so cast is correct
   @SuppressWarnings("unchecked") E result = (E) elements[--size];
   elements[size] = null; // Eliminate obsolete reference
   return result;
}

Here the author is talking about these different types of types in context of arrays . But through this question I would like to understand the difference between compile time types vs run time types for any type of object .

Geek
  • 26,489
  • 43
  • 149
  • 227
  • 1
    Actually, there are three types of type checking: Compile time, verification time, and runtime. Runtime is the least common type and basically only occurs where there is an explicit cast operation. – Hot Licks Feb 19 '13 at 17:43
  • @HotLicks Interfaces are also checked at runtime rather than verification time. But since the compiler checks them too, you don't have to worry about it except in cases of reflection or non Java code. – Antimony Feb 19 '13 at 17:45
  • @Antimony - Well, interfaces are treated like classes for type checking, of course. There is a dynamic lookup when an interface method is invoked, but a lookup failure would be essentially an "internal error", if I'm remembering correctly. – Hot Licks Feb 19 '13 at 17:50
  • @HotLicks Not during verification. The Hotspot JVM lets you treat any object as implementing any interface for verification purposes with no casts required. You'll get an exception at the point where you try to invoke an interface method on it. Incidentally, this makes marker interfaces problematic. – Antimony Feb 19 '13 at 17:52
  • @Antimony - I'm trying to remember the details of that, and I'm thinking maybe you're right that Sun made that change in version 4 or 5, presumably to make it easier to implement generics. Seemed like a step backwards. (I always felt that generics were not well thought-out.) (And Sun redid the rules for interfaces several times, in terms of when/how abstract methods are recognized, et al.) – Hot Licks Feb 19 '13 at 17:58
  • @HotLicks this has nothing to do with generics. Generics don't even exist at the bytecode level (except in optional annotations for reflection purposes). It was a decision presumably made to simplify the verifier design. – Antimony Feb 19 '13 at 17:59
  • @Antimony - Yet the verifier was working fine with the old scheme. (And they then went and really messed up the verifier -- was it in 5? -- because they didn't know how to verify `BigUglyMethod` efficiently with their old scheme. It was a PITA to keep in compliance with those changes.) – Hot Licks Feb 19 '13 at 18:02

5 Answers5

19

Java is a statically typed language, so the compiler will attempt to determine the types of everything and make sure that everything is type safe. Unfortunately static type inference is inherently limited. The compiler has to be conservative, and is also unable to see runtime information. Therefore, it will be unable to prove that certain code is typesafe, even if it really is.

The run time type refers to the actual type of the variable at runtime. As the programmer, you hopefully know this better than the compiler, so you can suppress warnings when you know that it is safe to do so.

For example, consider the following code (which will not compile)

public class typetest{
    public static void main(String[] args){
        Object x = args;
        String[] y = x;

        System.out.println(y[0])
    }
}

The variable x will always have type String[], but the compiler isn't able to figure this out. Therefore, you need an explicit cast when assigning it to y.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • Interestingly, in that case the JVM verifier would probably figure it out, even though the compiler wouldn't. The JVM verifier infers type from all assignments to a local variable, and given only the one, the type is well-known. – Hot Licks Feb 19 '13 at 17:54
  • Yep, this is only a problem during compilation. Of course if you stored it to a field of type Object and then retrieved it, then the verifier wouldn't be able to figure it out either. – Antimony Feb 19 '13 at 17:57
  • 1
    At compile time, Java believes the reference `x` is pointing to an object of type `Object` considering reference `x` is of type `Object`. It does not take into consideration the implicit upcast from `String` to `Object` of `args` which means `x` is actually pointing to an object of type `String[]`. At runtime, Java realizes the reference `x` is actually pointing to an object of type `String[]`. Thus, Java will give errors if you don't cast `x` to tell the compiler what `x` is actually pointed to at compile time. – NoName Jun 22 '17 at 18:02
2

An example

Number x;

if (userInput.equals("integer")) {
    x = new Integer(5);
} else {
    x = new Float(3.14);
}

There are two types related to x

  • the type of the name x. In the example, it's Number. This is determined at compile-time and can never change, hence it's the static type
  • the type of the value x refers to. In the example, it can be Integer or Float, depending on some external condition. The compiler cannot know the type at compilation time. It is determined at runtime (hence dynamic type), and may change multiple times, as long as it's a subclass of the static type.
blue_note
  • 27,712
  • 9
  • 72
  • 90
1

Java is statically typed. That means that every expression (including variables) in the language has a type that is known at compile time according to the rules of the language. This is known as the static type (what you call "compile-time type"). The types in Java are the primitive types and the reference types.

Also, each object at runtime in Java has a "class" (here, "class" includes the fictitious array "classes"), which is known at runtime. The class of an object is the class that an object was created with.

Part of the confusion comes from the fact that each class (and interface and array type) in Java has a corresponding reference type, with the name of the class (or interface or array type). The value of a reference type is a reference, which can either be null or point to an object. The Java language is designed so that a reference of reference type X, if not null will always point to an object whose class is the class X or a subclass thereof (or for interfaces, whose class implements the interface X).

Note that the runtime class applies objects, but objects are not values in Java. Types, on the other hand, apply to variables and expressions, which are compile-time concepts. A variable or expression can never have the value of an object, because there are no object types; it can have a value of a reference that points to an object.

newacct
  • 119,665
  • 29
  • 163
  • 224
1

Java arrays are so called "covariant", which means a String[] is a subtype of Object[], and type rules are checked at COMPILE time.

Java arrays check at RUNTIME if the object (e.g. String, Integer, WhatEver) you would like to store into it, is compatible with the type the array actually created with.

For example:

String[] strings = new String[2];
strings[0] = "I am text"; 
Object[] objects = strings;
objects[1] = new Date(); // Compiles, but at runtime you get an ArrayStoreException
0

I think of "compile time type" as ANY type a variable can be shown to have at compile time. That would include the declared class, any superclasses, and any implemented interfaces.

At runtime, a given object only has one lowest-level class; it can legally be cast to or assigned to a variable of that class, but also to any variable of any of its subclasses or implemented interfaces. The compiler will (often, anyway) allow you to cast it to anything, but the runtime will throw an exception if you attempt to assign something that is not legal.

Once an object is assigned to the variable, then the compiler will treat it as though it is of the type of the variable. So another use of "compile time" could be the variable type, and you can get around that at compile time by casting to a different type as long as you know the cast will be legal at runtime.

If I speak of just one type, I think of the 'runtime type' of a variable as the actual bottom (top?) level subclass of the variable; the lowest sub-class to which it could be cast. But I also routinely think of any object as an instantiation of any of its legal types.

Hope that helps.

arcy
  • 12,845
  • 12
  • 58
  • 103
  • A variable of class type holds a *reference*; it just has one type--the minimally-constrained kind of thing it will accept, which is determined at compile time. Array slots behave as variables, but their type is determined at runtime. If `myAnimals` is an `Animal[]`, which holds a reference to a `Cat[1]`, and `Cat[0] is an instance of `SiameseCat`, then the type `Cat` would be run-time-associated with the array elements, independent of the objects to which the array holds references. – supercat Apr 27 '14 at 20:13