Java SafeVarargs annotation, does a standard or best practice exist? - java

I've recently come across the java #SafeVarargs annotation. Googling for what makes a variadic function in Java unsafe left me rather confused (heap poisoning? erased types?), so I'd like to know a few things:
What makes a variadic Java function unsafe in the #SafeVarargs sense (preferably explained in the form of an in-depth example)?
Why is this annotation left to the discretion of the programmer? Isn't this something the compiler should be able to check?
Is there some standard one must adhere to in order to ensure his function is indeed varags safe? If not, what are the best practices to ensure it?

1) There are many examples on the Internet and on StackOverflow about the particular issue with generics and varargs. Basically, it's when you have a variable number of arguments of a type-parameter type:
<T> void foo(T... args);
In Java, varargs are a syntactic sugar that undergoes a simple "re-writing" at compile-time: a varargs parameter of type X... is converted into a parameter of type X[]; and every time a call is made to this varargs method, the compiler collects all of the "variable arguments" that goes in the varargs parameter, and creates an array just like new X[] { ...(arguments go here)... }.
This works well when the varargs type is concrete like String.... When it's a type variable like T..., it also works when T is known to be a concrete type for that call. e.g. if the method above were part of a class Foo<T>, and you have a Foo<String> reference, then calling foo on it would be okay because we know T is String at that point in the code.
However, it does not work when the "value" of T is another type parameter. In Java, it is impossible to create an array of a type-parameter component type (new T[] { ... }). So Java instead uses new Object[] { ... } (here Object is the upper bound of T; if there upper bound were something different, it would be that instead of Object), and then gives you a compiler warning.
So what is wrong with creating new Object[] instead of new T[] or whatever? Well, arrays in Java know their component type at runtime. Thus, the passed array object will have the wrong component type at runtime.
For probably the most common use of varargs, simply to iterate over the elements, this is no problem (you don't care about the runtime type of the array), so this is safe:
#SafeVarargs
final <T> void foo(T... args) {
for (T x : args) {
// do stuff with x
}
}
However, for anything that depends on the runtime component type of the passed array, it will not be safe. Here is a simple example of something that is unsafe and crashes:
class UnSafeVarargs
{
static <T> T[] asArray(T... args) {
return args;
}
static <T> T[] arrayOfTwo(T a, T b) {
return asArray(a, b);
}
public static void main(String[] args) {
String[] bar = arrayOfTwo("hi", "mom");
}
}
The problem here is that we depend on the type of args to be T[] in order to return it as T[]. But actually the type of the argument at runtime is not an instance of T[].
3) If your method has an argument of type T... (where T is any type parameter), then:
Safe: If your method only depends on the fact that the elements of the array are instances of T
Unsafe: If it depends on the fact that the array is an instance of T[]
Things that depend on the runtime type of the array include: returning it as type T[], passing it as an argument to a parameter of type T[], getting the array type using .getClass(), passing it to methods that depend on the runtime type of the array, like List.toArray() and Arrays.copyOf(), etc.
2) The distinction I mentioned above is too complicated to be easily distinguished automatically.

For best practices, consider this.
If you have this:
public <T> void doSomething(A a, B b, T... manyTs) {
// Your code here
}
Change it to this:
public <T> void doSomething(A a, B b, T... manyTs) {
doSomething(a, b, Arrays.asList(manyTs));
}
private <T> void doSomething(A a, B b, List<T> manyTs) {
// Your code here
}
I've found I usually only add varargs to make it more convenient for my callers. It would almost always be more convenient for my internal implementation to use a List<>. So to piggy-back on Arrays.asList() and ensure there's no way I can introduce Heap Pollution, this is what I do.
I know this only answers your #3. newacct has given a great answer for #1 and #2 above, and I don't have enough reputation to just leave this as a comment. :P

#SafeVarargs is used to indicate that methods will not cause heap pollution.
Heap pollution is when we mix different parameterized types in generic array.
For example:
public static <T> T[] unsafe(T... elements) {
return elements;
}
Object [] listOfItems = unsafe("some value", 34, new ArrayList<>());
String stringValue = (String) listOfItems[0]; // some value
String intValue = (String) listOfItems[1]; // ClassCastException
As you can see, such implementation could easily cause ClassCastException if we don't guess with the type.

Related

Instantiating generic type ArrayList<T>

I am new to generics and read in a article "A parameterized type, such as ArrayList<T>, is not instantiable — we cannot create instances of them".
Full quote, from Java in a Nutshell:
A parameterized type, such as ArrayList<T>, is not instantiable - we
cannot create instances of them. This is because <T> is just a type
parameter - merely a place-holder for a genuine type. It is only when
we provide a concrete value for the type parameter, (e.g.,
ArrayList<String>), that the type becomes fully formed and we can
create objects of that type.
This poses a problem if the type that we want to work with is unknown
at compile time. Fortunately, the Java type system is able to
accommodate this concept. It does so by having an explicit concept of
the unknown type which is represented as <?>.
I understand that it should not be instantiable since the concrete (actual) type is not known. If so, why does the below code compiles without an error?
public class SampleTest {
public static <T> List<T> getList(T... elements) {
List<T> lst = new ArrayList<>(); // shouldn't this line return an error?
return lst;
}
}
I know there is a gap in my understanding of generics here. Can someone point out what am i missing here?
Because T is given as another generic type argument.
It's the whole purpose of generics to make the type parameterizeable. So the caller can specify the type. This can be done in multiple layers: the caller may also be generic and let its caller specify the type.
public static void main(String[] args)
{
foo(7);
}
public static <T> void foo(T value)
{
bar(value);
}
public static <U> void bar(U value)
{
baz(value);
}
public static <V> void baz(V value)
{
System.out.println(value.getClass().getSimpleName());
}
It prints out
Integer
A parameterized type, such as ArrayList<T>, is not instantiable
Means: You cannot create ArrayList of an unknown T. It must be specified at compile time. But it can be done indirectly, by another generic. In your case, it's another T, which will be specified again by the caller of your generic getList.
The wildcard <?> is something different. It is used to specify compatibility. <?> is the syntax to avoid specification of the type. You can use extends to require a basetype or interface. However, you cannot create instances with wildcards.
List<?> list = new ArrayList<String>();
list = new ArrayList<Integer>();
This wouldn't be possible otherwise. It makes most sense when using it in parameter specifications, for instance:
public static int foo(List<? extends Comparable> list)
{
return list.get(1).compareTo(list.get(2));
}
It's very confusing of this book. It assumes that <?> somehow solves the problem that a List with unknown T cannot be instantiated. IMHO, this is rubbish. T must be specified to create an instance.
The code that you mention can compile because the Object "lst" is not actually initialized until the method is called. Since the method knows that it will be getting a var-args argument of type T, it can compile in this scenario. Take the example Wrapper class below for example:
public class Wrapper<T> {
public static <T> List<T> getList(T... elements){
List<T> lst = new ArrayList<>();
for(T element: elements) {
lst.add(element);
}
return lst;
}
}
This code can compile because the method hasn't been called. When the method is called, Type T will be the type that we pass as the var-args argument and the code will have no issue compiling. Lets test this in our main method:
public static void main( String[] args ){
System.out.println(Wrapper.getList("Hi", "Hello", "Yo"));
}
And the output is:
[Hi, Hello, Yo]
However, lets generate a compile-time error to see what the article is talking about within our main method:
Wrapper<T> myWrap = new Wrapper<>();
We are actually trying initialize a generic Object of the Wrapper class in the code above, but is unknown. Since the value for the placeholder will be unknown even when we call the method, it results in a compile-time error, whereas creating a List of type T within the getList method does not cause a compile-time error because it will be initialized with a type when the method is called.
once you call the method -> you are using a concrete value.
the method defines T and later you use it in the return type and the parameter list.
public static <T> List<T> getList(T... elements)
once you will send the first parameter from specific type -> the contract will force you for the next parameters.
List<? extends Object> list = getList("", 1); -> in this case java doesnt find common between string and integer so it uses the most basic connection "Object"
List<String> list2 = getList("test", "test2"); -> here you can see that because all of the parameters are Strings - java find that in common and use it as the T.
The specific passage from the book doesn't make any sense and is wrong. new ArrayList<T>() is perfectly fine provided that we are in the scope of a type parameter named T (either a type parameter of a generic class that we are in, or a type parameter of the generic method we are in).
new ArrayList<T>() can no less be instantiated than new ArrayList<String>() -- both compile to the same bytecode and both just instantiate an ArrayList object at runtime. The object doesn't know anything about its type parameter at runtime, and therefore no knowledge of T at runtime is needed to instantiate it. The type parameter in an instantiation expression of a parameterized type (new ArrayList<T>) is just used by the compiler to type-check the parameters passed to the constructor (there are none in this case) and to figure out the type returned by the expression; it is not used in any other way.
And by the way the method does not need to receive any parameters of type T, or of any type containing T, in order for this to work. A method that receives no arguments can still instantiate and return an ArrayList<T> perfectly fine:
public static <T> List<T> emptyList() {
List<T> lst = new ArrayList<T>();
return lst;
}
Also, the section in the book where this statement appears in doesn't really have anything to do with instantiation -- the section is about wildcards, and wildcards don't really have anything to do with object instantiation at all. So I am not really sure why they are mentioning it (incorrectly) there.

What is an apparently correct example of Java code causing heap pollution?

I'm trying to decide what to do every time I get a Java heap pollution warning when using parameterized varargs such as in
public static <T> LinkedList<T> list(T... elements) {
...
}
It seems to me that if I am confident not to be using some weird casts in my methods, I should just use #SafeVarargs and move on. But is this correct, or do I need to be more careful? Is there apparently correct code that is actually not safe when using parameterized varargs?
Reading about the subject, I notice that the provided examples are quite artificial. For example, the Java documentation shows the following faulty method:
public static void faultyMethod(List<String>... l) {
Object[] objectArray = l; // Valid
objectArray[0] = Arrays.asList(42);
String s = l[0].get(0); // ClassCastException thrown here
}
which is didactic but pretty unrealistic; experienced programmers are not likely to write code doing stuff like this. Another example is
Pair<String, String>[] method(Pair<String, String>... lists) {
Object[] objs = lists;
objs[0] = new Pair<String, String>("x", "y");
objs[1] = new Pair<Long, Long>(0L, 0L); // corruption !!!
return lists;
}
which is again pretty obviously mixing types in an unrealistic way.
So, are there more subtle cases in which heap pollution happens under parameterized varargs? Am I justified in using #SafeVarargs if I am not casting variables in a way that loses typing information, or mixes types incorrectly? In other words, am I justified in treating this warning as a not very important formality?
Good question. This has bothered me quite a while too. There are two things here - you don't care about the actual runtime type of the elements within the array, like the example that you have shown:
public static <T> LinkedList<T> list(T... elements) {
// suppose you iterate over them and add
}
This is where #SafeVarargs is well, safe.
And the second one is where you DO care about the runtime type of the elements within the array (even if so by accident). Arrays, in java, can not be generic, so you can not create a type T [] ts = new T[10], but you can declare a type T[] ts... and because arrays are covariant you can cast an Object[] to a T[] - if you know the types match.
All this becomes interesting when you pass a generic array:
// create a single element "generic" array
static <T> T[] singleElement(T elem) {
#SuppressWarnings("unchecked")
T[] array = (T[]) new Object[] { elem };
return self(array);
}
// #SafeVarargs
static <T> T[] self(T... ts) {
return ts;
}
Invoking this with Integer[] ints = singleElement(1); looks perfectly legal, but will break at runtime, this is where placing #SafeVarargs would be unsafe.
It will break because that cast (T[]) is actually useless and does not enforce any compile time checks. Even if you rewrote that method as:
static <T> T[] singleElement(T elem) {
#SuppressWarnings("unchecked")
T[] array = (T[]) new Object[]{elem};
System.out.println(array.getClass());
return array;
}
it would still not work.
To declare generic arrays T[] in Java is problematic because their type is not known at compile time and as a consequence they can be misused, as the examples in the question show. So the Java compiler issues warnings whenever this is done.
For example, if we declare a generic array as in
T[] tArray = (T[]) new Object[] { 42 };
we get an "unchecked cast" warning.
Besides such casts, the only other way of introducing a generic array into a program is by using a generic varargs. For example, in
void bar() {
foo(new Integer[]{ 42 })
}
void foo(T... args) {
}
Again here a generic array is being introduced, but in a different way than an unchecked cast, so it gets its own specific warning to make sure the user is not misusing it.
Indeed, as long as one is not converting the array to an array of a different type, it seems that using #SafeVarargs should be safe to use, barring atypical type conversions.

Understanding Principle of Truth In Advertising Java Generics

I have been trying to understand Java generics properly.So in this quest I have come accross one principle " Principle of Truth In Advertising", I am tring to understand this in simple language.
The Principle of Truth in Advertising: the reified type of an array must be a subtype
of the erasure of its static type.
I have written sample code .java and .class files as follows.Please go through code and please explain what part(in code) designates/indicates what part of above statement.
I have written comments to I think I should not write description of code here.
public class ClassA {
//when used this method throws exception
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
public static <T> T[] toArray(Collection<T> collection) {
//Array created here is of Object type
T[] array = (T[]) new Object[collection.size()];
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
//Working fine , no exception
public static <T> T[] toArray(Collection<T> collection, T[] array) {
if (array.length < collection.size()) {
//Array created here is of correct intended type and not actually Object type
//So I think , it inidicates "the reified type of an array" as type here lets say String[]
// is subtype of Object[](the erasure ), so actually no problem
array = (T[]) Array.newInstance(array.getClass().getComponentType(), collection.size());
}
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B");
String[] strings = toArray(list);
// String[] strings = toArray(list,new String[]{});
System.out.println(strings);
}
}
Please try to explain in simple language.Please point out where I am wrong. Corrected code with more comments is appreciated.
Thank you all
I refer to Java Generics and Collections as the Book and the Book's authors as the Authors.
I would upvote this question more than once as the Book makes a poor job of explaining the principle IMO.
Statement
Principle of Truth in Advertising:
the reified type of an array must be a subtype of the erasure of its static type.
Further referred to as the Principle.
How does the principle help?
Follow it and the code will compile and run without exceptions
Do not follow it and the code will compile, but throw an exception at runtime.
Vocabulary
What is a static type?
Should be called the reference type.
Provided A and B are types, in the following code
A ref = new B();
A is the static type of ref (B is the dynamic type of ref). Academia parlance term.
What is the reified type of an array?
Reification means type information available at runtime. Arrays are said to be reifiable because the VM knows their component type (at runtime).
In arr2 = new Number[30], the reified type of arr2 is Number[] an array type with component type Number.
What is the erasure of a type?
Should be called the runtime type.
The virtual machine's view (the runtime view) of a type parameter.
Provided T is a type parameter, the runtime view of the following code
<T extends Comparable<T>> void stupidMethod(T[] elems) {
T first = elems[0];
}
will be
void stupidMethod(Comparable[] elems) {
Comparable first = elems[0];
}
That makes Comparable the runtime type of T. Why Comparable? Because that's the leftmost bound of T.
What kind of code do I look at so that the Principle is relevant?
The code should imply assignment to a reference of array type. Either the lvalue or the rvalue should involve a type parameter.
e.g. provided T is a type parameter
T[] a = (T[])new Object[0]; // type parameter T involved in lvalue
or
String[] a = toArray(s); // type parameter involved in rvalue
// where the signature of toArray is
<T> T[] toArray(Collection<T> c);
The principle is not relevant where there are no type parameters involved in either lvalue or rvalue.
Example 1 (Principle followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Number[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation provides the answer: an array with component type Number is created using new. Number[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
Example 2 (Principle not followed)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Object[0];
}
Q1: What is the reified type of the array ts is referencing?
A1: Array creation using new, component type is Object, therefore Object[].
Q2: What is the static type of ts?
A2: T[]
Q3: What is the erasure of the static type of ts?
A3: For that we need the erasure of T. Given that T extends Number is bounded, T's erasure type is its leftmost boundary - Number. Now that we know the erasure type for T, the erasure type for ts is Number[]
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of Number[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 3 (Principle not followed)
Given the method providing an array
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
client code
List<String> s = ...;
String[] arr = toArray(s);
Q1: What is the reified type of the array returned by the providing method?
A1: for that you need too look in the providing method to see how it's initialized - new Object[...]. That means the reified type of the array returned by the method is Object[].
Q2: What is the static type of arr?
A2: String[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type String[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Object[] a subtype of String[]? No => That means the Principle is not followed.
Expect an exception to be thrown at runtime.
Example 4 (Principle followed)
Given the method providing an array
<T> T[] toArray(Collection<T> initialContent, Class<T> clazz){
T[] result = (T[]) Array.newInstance(clazz, initialContent);
// Copy contents to array. (Don't use this method in production, use Collection.toArray() instead)
return result;
}
client code
List<Number> s = ...;
Number[] arr = toArray(s, Number.class);
Q1: What is the reified type of the array returned by the providing method?
A1: array created using reflection with component type as received from the client. The answer is Number[].
Q2: What is the static type of arr?
A2: Number[]
Q3: What is the erasure of the static type of ts?
A3: No type parameters involved. The type after erasure is the same as the static type Number[].
Q4: Is the Principle followed?
A4: restating the question. Is A1 a subtype of A3? i.e. is Number[] a subtype of Number[]? Yes => That means the Principle is followed.
What's in a funny name?
Ranting here. Truth in advertising may mean selling what you state you are selling.
In
lvalue = rvalue we have rvalue as the provider and lvalue as the receiver.
It might be that the Authors thought of the provider as the Advertiser.
Referring to the providing method in Example 3 above,
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
the method signature
<T> T[] toArray(Collection<T> c);
may be read as an advertisement: Give me a List of Longs and I will give you an array of Longs.
However looking in the method body, the implementation shows that the method is not being truthful, as the array it creates and returns is an array of Objects.
So toArray method in Example 3 lies in its marketing campaigns.
In Example 4, the providing method is being truthful as the statement in the signature (Give me a collection and its type parameter as a class literal and I will give you an array with that component type) matches with what happens in the body.
Examples 3 and 4 have method signatures to act as advertisement.
Examples 1 and 2 do not have an explicit advertisement (method signature). The advertisement and the provision are intertwined.
Nevertheless, I could think of no better name for the Principle. That is a hell of a name.
Closing remarks
I consider the statement of the principle unnecessarily cryptic due to use of terms like static type and erasure type. Using reference type and runtime type/type after erasure, respectively, would make it considerably easier to grasp to the Java layman (like yours truly).
The Authors state the Book is the best on Java Generics [0]. I think that means the audience they address is a broad one and therefore more examples for the principles they introduce would be very helpful.
[0] https://youtu.be/GOMovkQCYD4?t=53
Think of it that way:
T[] array = (T[]) new Object[collection.size()]; A new Array is created. Due to language design, the type of T is unkown during runtime. In your example you know for a fact T is String, but the from the viewpoint of the vm T is Object. All casting operations are happening in the calling method.
So in toArray an array Object[] is created. The type parameter is more or less syntactic sugar which has no consequence for the bytecode created.
So why can't an array of objects be casted to an array of strings?
Let's have an example:
void methodA(){
Object[] array = new Object[10];
array[0]=Integer.valueOf(10);
array[1]=Object.class;
array[2]=new Object();
array[3]="Hello World";
methodB((String[])array);
}
void methodB(String[] stringArray){
String aString=stringArray[1]; //This is not a String, but Object.class!
}
If you could cast an array, you'd say "all elements I've added before are of a valid subtype". But since your array is of type Object, the vm can't guarantee the array will always under all circumstances contain valid subtypes.
methodB thinks it deals with an array of Strings, but in reality the array does contain very different types.
The other way around does not work either:
void methodA(){
String[] array = new String[10];
array[0]="Hello World";
methodB((Object[])array);
//Method B had controll over the array and could have added any object, especially a non-string!
System.out.println(array[1]);
}
void methodB(Object[] oArray){
oArray[1]=Long.valueOf(2);
}
I hope this helps a little bit.
Edit: After reading your question again, I think you are mixing to things:
Arrays can't be casted (as I explained above)
The cited sentence does say in plain English: "If you create an array of type A, all elements in this array must be of type A or a of a subtype of A". So if you create an array of Object you can put any java object into to array, but if you create an array of Number the values have to be of type Number (Long, Double, ...). All in all the sentence is rather trivial. Or I didn't understand it either ;)
Edit 2: As a matter of fact you can cast an array to any type you want. That is, you can cast an array as you can cast any type to String (String s=(String)Object.class;).
Especially you can cast a String[] to an Object[] and the other way around. As I pointed out in the examples, this operation introduces potential bugs in great numbers, since reading/writing to the array will likely fail. I can think of no situation where it is a good decision to cast an array. There might be situations (like generalized utility classes) where it seems to be a good solution, but I still would suggest to overthink the design if you find yourself in a situation where you want to cast an array.
Thanks to newacct for pointing out the cast operation itself is valid.

What's the reason I can't create generic array types in Java?

What's the reason why Java doesn't allow us to do
private T[] elements = new T[initialCapacity];
I could understand .NET didn't allow us to do that, as in .NET you have value types that at run-time can have different sizes, but in Java all kinds of T will be object references, thus having the same size (correct me if I'm wrong).
What is the reason?
It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array.
Quote:
Arrays of generic types are not
allowed because they're not sound. The
problem is due to the interaction of
Java arrays, which are not statically
sound but are dynamically checked,
with generics, which are statically
sound and not dynamically checked.
Here is how you could exploit the
loophole:
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static void main(String[] args) {
Box<String>[] bsa = new Box<String>[3];
Object[] oa = bsa;
oa[0] = new Box<Integer>(3); // error not caught by array store check
String s = bsa[0].x; // BOOM!
}
}
We had proposed to resolve this
problem using statically safe arrays
(aka Variance) bute that was rejected
for Tiger.
-- gafter
(I believe it is Neal Gafter, but am not sure)
See it in context here: http://forums.sun.com/thread.jspa?threadID=457033&forumID=316
By failing to provide a decent solution, you just end up with something worse IMHO.
The common work around is as follows.
T[] ts = new T[n];
is replaced with (assuming T extends Object and not another class)
T[] ts = (T[]) new Object[n];
I prefer the first example, however more academic types seem to prefer the second, or just prefer not to think about it.
Most of the examples of why you can't just use an Object[] equally apply to List or Collection (which are supported), so I see them as very poor arguments.
Note: this is one of the reasons the Collections library itself doesn't compile without warnings. If this use-case cannot be supported without warnings, something is fundamentally broken with the generics model IMHO.
The reason this is impossible is that Java implements its Generics purely on the compiler level, and there is only one class file generated for each class.
This is called Type Erasure.
At runtime, the compiled class needs to handle all of its uses with the same bytecode. So, new T[capacity] would have absolutely no idea what type needs to be instantiated.
The answer was already given but if you already have an Instance of T then you can do this:
T t; //Assuming you already have this object instantiated or given by parameter.
int length;
T[] ts = (T[]) Array.newInstance(t.getClass(), length);
Hope, I could Help,
Ferdi265
The main reason is due to the fact that arrays in Java are covariant.
There's a good overview here.
I like the answer indirectly given
by Gafter. However, I propose it is wrong. I changed Gafter's code a little. It compiles and it runs for a while then it bombs where Gafter predicted it would
class Box<T> {
final T x;
Box(T x) {
this.x = x;
}
}
class Loophole {
public static <T> T[] array(final T... values) {
return (values);
}
public static void main(String[] args) {
Box<String> a = new Box("Hello");
Box<String> b = new Box("World");
Box<String> c = new Box("!!!!!!!!!!!");
Box<String>[] bsa = array(a, b, c);
System.out.println("I created an array of generics.");
Object[] oa = bsa;
oa[0] = new Box<Integer>(3);
System.out.println("error not caught by array store check");
try {
String s = bsa[0].x;
} catch (ClassCastException cause) {
System.out.println("BOOM!");
cause.printStackTrace();
}
}
}
The output is
I created an array of generics.
error not caught by array store check
BOOM!
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Loophole.main(Box.java:26)
So it appears to me you can create generic array types in java. Did I misunderstand the question?
From Oracle tutorial:
You cannot create arrays of parameterized types. For example, the following code does not compile:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
The following code illustrates what happens when different types are inserted into an array:
Object[] strings = new String[2];
strings[0] = "hi"; // OK
strings[1] = 100; // An ArrayStoreException is thrown.
If you try the same thing with a generic list, there would be a problem:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed
stringLists[0] = new ArrayList<String>(); // OK
stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown,
// but the runtime can't detect it.
If arrays of parameterized lists were allowed, the previous code would fail to throw the desired ArrayStoreException.
To me, it sounds very weak. I think that anybody with a sufficient understanding of generics, would be perfectly fine, and even expect, that the ArrayStoredException is not thrown in such case.
In my case, I simply wanted an array of stacks, something like this:
Stack<SomeType>[] stacks = new Stack<SomeType>[2];
Since this was not possible, I used the following as a workaround:
Created a non-generic wrapper class around Stack (say MyStack)
MyStack[] stacks = new MyStack[2] worked perfectly well
Ugly, but Java is happy.
Note: as mentioned by BrainSlugs83 in the comment to the question, it is totally possible to have arrays of generics in .NET
class can declare an array of type T[], but it cannot directly instantiate such an array. Instead, a common approach is to instantiate an array of type Object[], and then make a narrowing cast to type T[], as shown in the following:
public class Portfolio<T> {
T[] data;
public Portfolio(int capacity) {
data = new T[capacity]; // illegal; compiler error
data = (T[]) new Object[capacity]; // legal, but compiler warning
}
public T get(int index) { return data[index]; }
public void set(int index, T element) { data[index] = element; }
}
It is because generics were added on to java after they made it, so its kinda clunky because the original makers of java thought that when making an array the type would be specified in the making of it. So that does not work with generics so you have to do
E[] array=(E[]) new Object[15];
This compiles but it gives a warning.
There surely must be a good way around it (maybe using reflection), because it seems to me that that's exactly what ArrayList.toArray(T[] a) does. I quote:
public <T> T[] toArray(T[] a)
Returns an array containing all of the
elements in this list in the correct order; the runtime type of the
returned array is that of the specified array. If the list fits in the
specified array, it is returned therein. Otherwise, a new array is
allocated with the runtime type of the specified array and the size of
this list.
So one way around it would be to use this function i.e. create an ArrayList of the objects you want in the array, then use toArray(T[] a) to create the actual array. It wouldn't be speedy, but you didn't mention your requirements.
So does anyone know how toArray(T[] a) is implemented?
If we cannot instantiate generic arrays, why does the language have generic array types? What's the point of having a type without objects?
The only reason I can think of, is varargs - foo(T...). Otherwise they could have completely scrubbed generic array types. (Well, they didn't really have to use array for varargs, since varargs didn't exist before 1.5. That's probably another mistake.)
So it is a lie, you can instantiate generic arrays, through varargs!
Of course, the problems with generic arrays are still real, e.g.
static <T> T[] foo(T... args){
return args;
}
static <T> T[] foo2(T a1, T a2){
return foo(a1, a2);
}
public static void main(String[] args){
String[] x2 = foo2("a", "b"); // heap pollution!
}
We can use this example to actually demonstrate the danger of generic array.
On the other hand, we've been using generic varargs for a decade, and the sky is not falling yet. So we can argue that the problems are being exaggerated; it is not a big deal. If explicit generic array creation is allowed, we'll have bugs here and there; but we've been used to the problems of erasure, and we can live with it.
And we can point to foo2 to refute the claim that the spec keeps us from the problems that they claim to keep us from. If Sun had more time and resources for 1.5, I believe they could have reached a more satisfying resolution.
As others already mentioned, you can of course create via some tricks.
But it's not recommended.
Because the type erasure and more importantly the covariance in array which just allows a subtype array can be assigned to a supertype array, which forces you to use explicit type cast when trying to get the value back causing run-time ClassCastException which is one of the main objectives that generics try to eliminate: Stronger type checks at compile time.
Object[] stringArray = { "hi", "me" };
stringArray[1] = 1;
String aString = (String) stringArray[1]; // boom! the TypeCastException
A more direct example can found in Effective Java: Item 25.
covariance: an array of type S[] is a subtype of T[] if S is a subtype of T
T vals[]; // OK
But, you cannot instantiate an array of T
// vals = new T[10]; // can't create an array of T
The reason you can’t create an array of T is that there is no way for the
compiler to know what type of array to actually create.
Try this:
List<?>[] arrayOfLists = new List<?>[4];

I'm studying Head First Java, but I can't understand Page 544

"When you declare a type parameter for the class, you can simply use that type any place that you'd use a real class or interface type. The type declared in the method argument is essentially replaced with the type you use when you instantiate the class.
If the class itself doesn't use a type parameter, you can still specify one for a method, by declaring it in a really unusual (but available) space-before the return type, This method says that T can be "any type of Animal"."
Can you explain?
What it means is that in a generic class, you can write methods like so:
public T doSomething () {
}
Note that the return type is 'T'.
At compile-time, the return type of that method will be whatever you have passed to the generic class when you instantiated it.
class Zoo<T> {
static void putAnimal(T animal) {
// do stuff
}
}
Zoo<Ape> apeZoo = new Zoo<Ape>(); // you can now put apes (and chimps) here
Zoo<Reptile> monkeyZoo = new Zoo<Reptile>(); // this zoo takes reptiles
apeZoo.putAnimal(new Chimp());
monkeyZoo.putAnimal(new Tortoise());
For the first paragraph, this is just how generics work for classes. For instance, for list, you can create a list of a generic type, such as integer, e.g.:
ArrayList<Integer> list = new ArrayList<Integer>();
(in real code you'd use List<Integer> of course)
Now ArrayList will be defined as:
public class Arraylist<T> { // implements....
// ...
public T get(int index) {
// ...
}
}
Which is what makes it possible to use the get method on list and get an Integer (because we made a class of type ArrayList<Integer> so T = Integer). Otherwise the compiler would have no idea what types of objects the list was storing and you'd have to get the method to return an Object, which is how it used to be.
What the second paragraph means is that you can add type parameters to methods just as you can to classes. e.g.:
public <T> void noOp(T element) {
// code here
}
This would allow you, for instance, to create a static utility method that returns something of type T. To return the first element of a List of T's:
public static <T> T getFirst (List<T> list) {
return list.get(0);
}
And you could use this method in a strongly typed fashion. Suggestions for better examples welcome. :-)
edit: I just realised I once wrote something that uses this functionality. I was using the JPA API and getting really annoyed at all the times you have to return something (a list, or a single item) from a query, and running into unchecked type warnings because there's no way to infer the type here. If you're like me and trying to avoid warnings in your code, you'd have to suppress the warnings every single time. So I wrote this method to suppress the warnings for me:
#SuppressWarnings("unchecked")
public static <T> List<T> returnResultList(Query query) {
return (List<T>)query.getResultList();
}
Which through the magic of type inference works on:
List<Integer> list = returnResultList(query);

Categories