I recently came across that, Arrays are reified in Java. That is, they know the type information only during run time. But I am a little confused with this definition.
If Arrays are said to know the type information only during runtime, I should literally be able to assign any values to any arrays, since the typing is known only at run time and errors will be thrown at run time only. But that is not the case in real time. We get a compile time error for that.
So can someone throw light on "what does it mean by - arrays are reified"?
What I think that means is that the given lines of code will throw an exception:
String[] arrayOfStrings = new String[10];
Object[] arrayOfObjects = arrayOfStrings; // compiles fine
arrayOfObjects[0] = new Integer(2); // throws a runtime exception (ArrayStoreException IIRC)
Arrays are covariant: String[] extends Object[]. But the actual type of the array is known at runtime, and an attempt to store an instance which is not of the right type throws an exception.
I believe the term you are looking for is reifiable.
A reifiable type does not lose any type information due to type erasure at runtime. Examples of reifiable types include:
primitives
non-generic reference types
arrays or primitives or arrays
of non-generic reference types.
Reifiable does not mean a type is not known at compile time. What this does mean is that something like the following, cannot be typed checked:
List<Integer>[] myList;
Arrays carry runtime information about the types they store. Non-refiable types cannot be type checked at runtime, which does not make them good candidates for the component type of an array.
When using reifiable types as the component type of an array such as String[] the complete type information is available at runtime, so type checks can be performed.
String[] someArray = new String[2];
//some factory returns Integer upcasted to Object
someArray[0] = someFactory.getType("Integer"); //throws RuntimeException
Sources:
http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ106 (Good)
If Arrays are said to know the type information only during runtime, I should literally be able to assign any values to any arrays, since the typing is known only at run time errors will be thrown at run time only. But that is not the case in real time. We get a compile time error for that.
Reifiable types know their type at runtime and compile time, which is why the compiler will still prevent you making silly mistakes where it can (what's the point in letting them through?)
However, there's times when the compiler can't always work out for certain whether an assignment (for instance) will be valid because it doesn't know the exact types, and this is where reified types can check. For instance:
Object[] arr = new String[5];
arr[0] = 7;
...this will compile, because on the second line the compiler only knows the static type of the array as Object, whereas the dynamic type is something more specific. It will fail as an exception at Runtime, which it can only do because (unlike with the generic collection classes) the specific type is known at runtime.
As mentioned in doc:
A reifiable type is a type whose type information is fully available
at runtime. This includes primitives, non-generic types, raw types,
and invocations of unbound wildcards.
Non-reifiable types are types where information has been removed at
compile-time by type erasure — invocations of generic types that are
not defined as unbounded wildcards. A non-reifiable type does not have
all of its information available at runtime. Examples of non-reifiable
types are List and List; the JVM cannot tell the
difference between these types at runtime. As shown in Restrictions on
Generics, there are certain situations where non-reifiable types
cannot be used: in an instanceof expression, for example, or as an
element in an array.
So Arrays are reified and covariant but generics are invariant and type-erased by nature. Arrays provide runtime type safety and throw ArrayStore exception if element of correct type is not added.
Related
public void run(){
setFont("Courier-24");
//Define list as ArrayList<Integer>
ArrayList<Integer> list = new ArrayList<Integer>();
readList(list);
}
private void readList(ArrayList list){
list.add("Hello");
list.add(2);
println("list = "+list);
println("Type of list = "+list.get(0).getClass());
println("Type of list = "+list.get(1).getClass());
}
Result:
list = [Hello, 2]
Type of list = class java.lang.String
Type of list = class java.lang.Integer
Here is my code and result. My question is, how is it possible that ArrayList of type Integer can store String objects? What's the type of list now? And what mechanism is this?
Java's generics don't actually change the underlying class or object, they just provide (mostly) compile-time semantics around them.
By passing an ArrayList<Integer> into a method expecting an ArrayList (which can hold anything), you're bypassing the compiler's ability to provide you with that type safety.
The Java Generics Tutorial explains this, and why Java implements generics this way. This page, in particular, focusses on it:
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
What that doesn't say is that this also allows code written with generics (like your run) to interact with code written without generics (like your readList), which is important when adding a feature to a very-well-established language with a huge library base (as they were when adding generics to Java).
When you declare:
private void readList(ArrayList list)
you are not specifying any type to this ArrayList so by default it is of type Object.
Both String and Integer (in fact all classes in java) are sub types of Object. Hence it is possible to add them to list.
For more information about generics without types please read here. In short generics types are for compile time checks only, so that you dont add wrong types (which can later cause exceptions. In this case your operations on String and Integer are compatible so luckily no errors).
In your readList method's parameter you haven't constrained it to only Integer value types, thus list doesn't get the benefits of compile-time checking and must resort to runtime type checking.
Declaration
ArrayList list
in the method readList is equivalent to the
ArrayList<Object> list
It's obvious that String is Object, as well as Integer. When passing to println, both will be toString'ed with their own methods.
I think that the ArrayList type without the generics specification you pass as argument is assument as ArrayList. Both String and Integer inherits Object so they both can be added in the list. However the ArrayList elements are of type Objects.
Using Java's Generics features I created a List object and on the left hand side I am using the raw type List where on the right hand side I am using the generic type ArrayList< String >.
List myList=new ArrayList<String>();
And I added one int value into the list object.
myList.add(101);
I was hoping that I will get some compilation error but this program is running fine.But if I use generic type List< String > on the left hand side and raw type ArrayList on the right hand side and try to add an int value into the list, I am getting compilation error as expected.
List<String> myList=new ArrayList();
myList.add(101);//The method add(int, String) in the type List<String> is not applicable for the arguments (int)
Why in Java generics right hand side type of the collection does not have any effect? And why Java allowing us to do so when it does not have any effect.I am using Java 1.6. Please explain.
If you don't supply a generic type parameter on the left-hand side, the List is declared as a raw type. This means the compiler doesn't know what is legal or not to store in that list, and is relying on the programmer to perform appropriate instanceof checks and casts.
Raw types also have the effect of obliterating all generic type information in the class they appear in.
The JLS provides a much more detailed look at raw types. You should be seeing a warning in your IDE or from the compiler about the assignment to a raw type as well:
To make sure that potential violations of the typing rules are always
flagged, some accesses to members of a raw type will result in
compile-time unchecked warnings. The rules for compile-time unchecked
warnings when accessing members or constructors of raw types are as
follows:
At an assignment to a field: if the type of the left-hand operand is a
raw type, then a compile-time unchecked warning occurs if erasure
changes the field's type.
At an invocation of a method or constructor: if the type of the class
or interface to search (§15.12.1) is a raw type, then a compile-time
unchecked warning occurs if erasure changes any of the formal
parameter types of the method or constructor.
No compile-time unchecked warning occurs for a method call when the
formal parameter types do not change under erasure (even if the result
type and/or throws clause changes), for reading from a field, or for a
class instance creation of a raw type.
Tom G's answer is nice and explains things in detail, but I get the feeling that you already know at least some of that stuff, because you said this:
I was hoping that I will get some compilation error
So, let me address precisely that part.
The reason you are not getting any compilation error is because generics were added as an afterthought in java, and for this reason many generics-related issues which ought to be errors have instead been demoted to warnings in order to not break existing code.
And what is most probably happening is that these warnings are turned off in your development environment.
Steps to correct the problem:
Go to the options of your IDE
Find the "warnings" section.
Enable EVERYTHING.
Pick your jaw from the floor after you have seen the enormous number
of warnings you get.
Disable all the warnings that do not make any sense, like "hard-coded string" or "member access was not qualified with this", keep everything else. Be sure that the one which says something like "Raw use of parameterized class" is among the ones you keep.
At a glance it looks like myList can only store String
At a glance, perhaps. But it's really important to realize that there is no such thing as "a list that can only store Strings", at least in the standard APIs.
There is only List, and you have to include the right instructions to the compiler to berate you if you try to add something that's not a String to it, i.e. by declaring it as List<String> myList.
If you declare it as "plain old List", the compiler has no instructions as to what to allow or disallow you to put into it, so you can store anything within the type bounds of the backing array, namely, any Object.
The fact that you say new ArrayList<String>() on the RHS of the assignment is irrelevant: Java doesn't attempt to track the value assigned to a variable. The type of a variable is the type you declare.
"... generics right hand side of type of the collection does not have any effect" is mostly true. When the "var" keyword is substituted for "List", the right hand side generic does have an effect. The code below creates an ArrayList of Strings.
var myList = new ArrayList<String>();
In computing, reification has come to mean an explicit representation
of a type—that is, run-time type information.
oracle tutorials says ,
A reifiable type is a type whose type information is fully available
at runtime. This includes primitives, non-generic types, raw types,
and invocations of unbound wildcards.
Non-reifiable types are types where information has been removed at
compile-time by type erasure — invocations of generic types that are
not defined as unbounded wildcards.
A type is reifiable if it is one of the following:
A primitive type (such as int) //understood
A nonparameterized class or interface type (such as Number, String, or Runnable) // why
A parameterized type in which all type arguments are unbounded wildcards (such as List<?>, ArrayList<?>, or Map<?, ?>) // why
A raw type (such as List, ArrayList, or Map) // why
An array whose component type is reifiable(such as int[], Number[], List<?>[], List[], or int[][]) // why
A type is not reifiable if it is one of the following:
A type variable(such as T) // why
A parameterized type with actual parameters (such as List<Number>, ArrayList<String>, or Map<String, Integer>) // why
A parameterized type with a bound (such as List<? extends Number> or Comparable<? super String>) // why
Why 2,3,4,5 is reifiable and 6,7,8 as non-reifiable?
Sun/Oracle says the reason is combo of:
Need: compile time type checking is sufficient
Code size: avoid STL-like code bloat
Performance: avoid type checking at runtime that was already done at compile
Type erasure ensures that no new classes are created for parameterized
types; consequently, generics incur no runtime overhead.
In short, 1-5 are reifiable because they simply remain the same types as specified in the code so there is no type information lost/erased, but 6-8 will lose type information during compilation (the stuff between the <>) so can't be accessed at runtime.
Understand the meaning of this two terms.
Reifiable means whose type is fully available at run time means java compiler do not need any process of type erasure.
Non-Reifiable means java compiler needs type erasure process because type is not fully available.
A type is reifiable if it is one of the following:
1. A primitive type (such as int) :
Here think that when you write or use any int as a reference, do you think that compiler needs any process for identification for the type of int? no because int is int.... same for all primitive type
2. A nonparameterized class or interface type (such as Number, String, or Runnable)
same answer as i told in previous answer that compiler do not need any type erasure for Number, String, or Runnable.
3. A parameterized type in which all type arguments are unbounded wildcards (such as List<?>, ArrayList<?>, or Map<?, ?>)
All unbounded wildcard are accepted as reifiable type because it is already mention in definition of reifiable type, now it is up to the API developer why they consider it as a reifiable type.
4. A raw type (such as List, ArrayList, or Map) ::
same answer as first question
5. An array whose component type is reifiable(such as int[], Number[], List<?>[], List[], or int[][]) ::
same answer as first question
A type is not reifiable if it is one of the following:
6. A type variable(such as T) :
Because java can not identify the type of T, Compiler needs type erasure to identify the type.
7. A parameterized type with actual parameters (such as List<Number>, ArrayList<String>, or Map<String, Integer>):
Here all type is a generic type, at runtime compiler see List as List ... so as per definition of Non-refiable all these collection are consider as a non reifiable.
8. A parameterized type with a bound (such as List<? extends Number> or Comparable<? super String>).
same answer as previous one
you could ask google the same question:
reifiable type
When you use generics, much of the time, compile-time type
information is lost. At run time, often all the program knows about a
reference is that is a reference to some sort of Object. If all the
type information is also known at run time, the type is called
reifiable. Perhaps some day generics will be redesigned so that all
types are reifiable.
A reifiable type is a type whose type information is fully available
at runtime. This includes primitives, non-generic types, raw types,
and invocations of unbound wildcards.
Non-reifiable types are types where information has been removed at
compile-time by type erasure — invocations of generic types that are
not defined as unbounded wildcards. A non-reifiable type does not have
all of its information available at runtime. Examples of non-reifiable
types are List<String> and List<Number>; the JVM cannot tell the
difference between these types at runtime. As shown in Restrictions on
Generics, there are certain situations where non-reifiable types
cannot be used: in an instanceof expression, for example, or as an
element in an array.
Reference
Java originally implemented reflection in version 1.1.
Generic classes were introduced in version 5.0.
When introducing generics it was decided that for backwards compatibility reasons the generic type information will be erased at runtime. This allowed code that was written pre generics to operate with generics based code without modification.
For example a List[Integer32] would be translated by the compiler to Integer32[].
All type checks would be done at compile time, and if anything was missed it would generate a runtime error.
This implementation detail meant that generics were not reified (there is no specific implementation of them in the VM) and therefore whenever one would try to reflect the generic type the returned information would be that of the underlying type. Another reason why this would be advantageous is because no implementations for the reified types would have to be emitted by the VM whenever one was used (in c# for instance, whenever you use a generic type it's actual implementation is generated by the VM at runtime, along with the reflection metadata, thus having a performance hit whenever a new type needed to be generated).
Just an educated guess, but I suspect the key to understanding this is to recognize that while Java is a strongly typed language and verifies type references as part of the compilation process, in many cases the type information is not actually needed to execute the logic. In that case, the generated bytecode may know that it is working with an instance of an Object, but not know the type. That would especially make sense given that languages that do not use strong typing can be generated as java bytecode. So if the object type has been dropped, the instance would be non-reifiable.
I'm not entirely sure I understand your question, but you might be referring to object types as opposed to primitive types. That question is all the more important as primitive types such as int or double cannot be used as generic types -- hence their wrapping classes such as Integer.
// This won't work
ArrayList<int> list = new ArrayList<int>();
// But this will
ArrayList<Integer> list = new ArrayList<Integer>();
To sum it up I'd say all objects -- and only objects -- are reifiable. (And therefore usable as generic types instantiation)
This is not a question how to do things. It is a question of why is it the way it is.
Arrays in Java know their component type at run time, and because of type erasure we cannot have array objects of generic type variables. Array types involving generics are allowed and checked for sound read/write, the only issue seem to be the allocator expressions.
Notice that the Java compiler also disallows the following:
Pong<Integer> lotsofpong [] = new Pong<Integer>[100];
...where Pong is just any old parametric class. There is nothing unknown here. Yes, at run-time, lotsofpong would just be an array of Pong, but I cannot see a reason why the compiler cannot remember the type parameter for compile-time purposes. Well, it actually does remember it, because those types exist at compile time, so the only problem seems to be the refusal to give the allocator at compile-time a particular generic-parameter-involving component type.
Even if the parameter of Pong was a generic type variable that should not make a difference either. The dynamic array would still be an array of Pong, requiring per element the size of a Pong, which does not depend on its type parameter.
Yes, I know there are ways around it - either use casts (perhaps with SuppressWarning) from the non-parametric type, or subclass Pong<Integer> with a non-parametric class and use that type instead. But is there a reason why this kind of allocator is not allowed?
Based on the link that was provided by Zeller (based on Josh Bloch - 'Effective Java Book').
Arrays are not safe because the following code will compile:
String strings [] = {"Foo"};
Object objects [] = strings;
objects[0] = 1;
You will get a special exception at run-time: java.lang.ArrayStoreException.
Java run-time cheks at run-time that you put an appropriate type into the array.
Assigning an array to an array of its super type is called 'Covariance'.
Generics are guaranteed to be safe at compile-time.
If the code snippet that you mentioned in the question was able to be compiled, the following code would also compiles:
Pong<Integer> integerPongs [] = new Pong<Integer>[100];
Object objectPongs [] = integerPongs;
objectPongs[0] = new Pong<String>();
Pong<Integer> stringPong = integerPongs[0]; // ClassCastException
Our code becomes not safe therefore it was forbidden by the specification.
The reason that :
objectPongs[0] = new Pong<String>();
does not throw java.lang.ArrayStoreException is because the run-time type of each instance of Pong is always Pong since Generics is a compile-time mechanism.
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 .
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.
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.
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.
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.
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