Arrays with generics in java

Creating Arrays of Generic Types in Java

So I know that you cannot «easily» create an array of a generic type in Java (but you can create collections). I recently ran across a situation where I needed a 2 dimensional array of objects (that were Generic). Here’s a «rough» idea of what it looked like (not complete but I’m trying to be as brief as possible):

class Outer  < private Foo[][] foo; abstract class Foo extends Blah < public ListgetContents (); > abstract class Bar extends Foo < . >> 
foo = (Foo[][])Array.newInstance (Foo.class, new int[]); 

which the compiler accepted though I had to suppress warnings. I guess my question is «Is this going to nip me in the bud somewhere down the line? The member «foo» is never exposed to the outside (though the types Foo and Bar are). I know it’s ugly but it definitely works and saved me from having to create some other «psedu-kludge» the would probably have caused classes overriding the «Outer» class more headaches. Thanks in advance!

This might make things easier to visualize

This is closer to what I’m actually doing; realizing of course that there are many support methods and other logic inside of the Map class that I’ve left out for brevity.

 import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; interface Cell  < public void add (T t); public boolean remove (T t); public ListgetAll (); public Map getMap (); > class Map  < protected BaseCell map[][]; public abstract class BaseCell implements Cell < private Listcontents; public BaseCell () < this.contents = new ArrayList(); > public void add (T t) < this.contents.add (t); >public boolean remove (T t) < return this.contents.remove (t); >public List getAll () < return this.contents; >public Map getMap () < return Map.this; >abstract public boolean test (); > public class SpecialCell extends BaseCell < @Override public boolean test() < return true; >> public class SpecialCell2 extends BaseCell < @Override public boolean test() < return false; >> @SuppressWarnings("unchecked") public Map (int width, int height) < this.map = (BaseCell[][])Array.newInstance(BaseCell.class, new int[] ); for (int y = 0; y < height; y++) < for (int x = 0; x < width; x++) < if (Math.random() < .5) < this.map[x][y] = new SpecialCell (); >else < this.map[x][y] = new SpecialCell2 (); >> > > public BaseCell getCellAt (int x, int y) < return this.map[x][y]; >> public class Junk < /** * @param args */ public static void main(String[] args) < class Occupant < >Map map = new Map (50, 50); map.getCellAt(10, 10).add(new Occupant ()); map.getCellAt(10, 10).getMap (); for (int y = 0; y < 50; y++) < for (int x = 0; x < 50; x++) < System.out.print (map.getCellAt (x, y).test () ? "1" : "0"); >System.out.println (); > > > 

Источник

Читайте также:  The complete javascript course

Generic arrays in Java

OK, I’ve been google’ing the web, and I just can’t seem to find any solution to my problem. I found lots of solutions, just not any that fit. I need to create an array of generics. But the generic type itself extends Comparable. When I try the following:

The problem is that the Object cannot be cast as a generic that extends Comparable. Is there a way around this?

5 Answers 5

Generics and arrays don’t mix, basically. The short answer is that you can work around this problem. The longer answer is that you probably shouldn’t and I’ll explain why.

private Comparable[] hashtable; . hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize); 

but you can’t create an array of your parameterized type.

Arrays are covariant. That means they retain the type of their elements at runtime. Java’s generics are not. They use type erasure to basically mask the implicit casting that is going on. It’s important to understand that.

So when you create an Object array you can’t cast it to, say, a Comparable array (or any other type) because that is not correct.

To give you an example. With generics this is perfectly legal:

List list = new ArrayList(); List list2 = (List)list; list.add(3); 

It’s also why you can’t do this:

ie at runtime there is no knowledge of T’s class. This is why the above code is more often written as:

public T newInstance(T t, Class clazz)

because their is no runtime type for the generic argument. But with arrays:

String arr[] = new String[10]; Integer arr2[] = (Integer[])arr; // error! 

What you should be doing in this case (imho) is not using arrays but using an ArrayList . In all honesty, there is very little reason to use arrays over an ArrayList and generics is just one example of that.

For a better and more complete explanation see the (excellent) Java Generics FAQ:

Can I create an array whose component type is a concrete parameterized type?

No, because it is not type-safe.

Arrays are covariant, which means that an array of supertype references is a supertype of an array of subtype references. That is, Object[] is a supertype of String[] and a string array can be accessed through a reference variable of type Object[] .

.

Источник

Java Generics Syntax for arrays

I think it should declare an array where each element is a List (e.g., a LinkedList or an ArrayList ) and require that each List contain ArrayList objects. My reasoning:

 List someList; // A List of String objects List someList; // A List of ArrayList objects List[] someListArray; // An array of List of ArrayList objects 

After running some tests, I determined that it accepts an array where each element is an LinkedList object and does not specify what the LinkedList objects contain. So List specifies what the List must contain, but List[] specifies how the List must be implemented. Am I missing something? Here are my tests.

import java.util.ArrayList; import java.util.List; import java.util.LinkedList; public class Generics1 < public static void main(String[] args) < List[] someListArray; someListArray = getArrayWhereEachElementIsAnArrayListObject(); // Why does this satisfy the declaration? //someListArray[0] => ArrayList object holding Strings someListArray= getArrayWhereEachElementIsAListOfArrayListObjects(); //someListArray[0] => ArrayList object holding ArrayList objects > public static List[] getArrayWhereEachElementIsAnArrayListObject() < List[] arrayOfLists = new ArrayList[2]; arrayOfLists[0] = getStringList(); arrayOfLists[1] = getIntegerList(); return arrayOfLists; >public static List[] getArrayWhereEachElementIsAListOfArrayListObjects() < List list1 = new ArrayList(); list1.add(getArrayList()); List list2 = new ArrayList(); list2.add(getArrayList()); List[] arrayOfListsOfArrayLists = new ArrayList[2]; arrayOfListsOfArrayLists[0] = list1; arrayOfListsOfArrayLists[1] = list2; return arrayOfListsOfArrayLists; >public static List getStringList() < List stringList= new ArrayList(); stringList.add("one"); stringList.add("two"); return stringList; >public static List getIntegerList() < List intList= new ArrayList(); intList.add(new Integer(1)); intList.add(new Integer(2)); return intList; >public static ArrayList getArrayList() < ArrayList arrayList = new ArrayList() ; return arrayList; >> 

It looks like all the generic type arguments got stripped from your code sample. This would have happened because you enclosed it in PRE tags, and the site interpreted the type arguments as malformed or forbidden HTML tags (as per Q31657). You should be able to edit it.

6 Answers 6

The answer is that arrays can only hold reified types. And generified classes are not reified. That is, the runtime «type» of the List is just List. Generics are erased at runtime (google «wall of erasure» for more).

There is no type-safe way to declare what you’re trying to declare. Generally, I’d recommend you use a List instead of an array in this case. Some people have gone so far as to suggest that arrays should be treated as deprecated types now that we have generics. I can’t say I’m willing to go that far but you should consider whether a collection is a better alternative whenever you’re drawn to an array.

The book Java Generics and Collections by Naftalin and Wadler is an excellent reference for questions you might have about generics. Or, of course, the Generics FAQ is your canonical online reference.

«Prefer lists to array because arrays are covariant and generics are invariant’

This may give some performance hit (not even noticable i bet) but you will get better type safety at compile time.

but I think the question should be more around «why» you need this?

I don’t «need» this. I ran across the declaration during code maintenance and was trying to understand what the declaration meant.

array of ( List of ArrayList ) 

But due to limitations in Java generics (bug 6229728) you can only actually create:

List[] someListArray = (List[]) new List[5]; 

List is a List capable of holding ArrayList objects List [] is an array of such Lists

So, what you said is that An Array of (List of ArrayList object) is CORRECT.

Can you share what your tests were. My own tests are different

import java.util.*; public class TestList < public static void main(String . args) < class MySpecialLinkedList extends LinkedList> < MySpecialLinkedList() < >public void foo() < >public Object clone() < return super.clone(); >> List> [] someListArray = new MySpecialLinkedList[10]; for (int i = 0; i < 10; ++i) < someListArray[i] = new LinkedList>(); for (int j = 0; j < 20; ++j) < someListArray[i].add(new ArrayList()); for (int k = 0; k < 30; ++k) < someListArray[i].get(j).add(j); >> > > > 

You are correct in saying:

After running some tests, I determined the declaration means an array where each element is an ArrayList object.

List[] myArray = new ArrayList[2]; myArray[0] = new ArrayList(); myArray[0].add("test 1"); myArray[1] = new ArrayList(); myArray[1].add("test 2"); print myArray; 

It seems to me there is no reason not to do this instead:

List myArray = new ArrayList(); 

After running some additional tests, I think I have my answer.

List[] does indeed specify an array where each element is a List of ArrayList objects.

Compiling the code as shown below revealed why my first test allowed me to use an array where each element is a List of anything. Using return types of List[] and List in the methods that populate the arrays did not provide the compiler enough information to prohibit the assignments. But the compiler did issue warnings about the ambiguity.

From the compiler’s point of view, a method returning a List[] might be returning a List (which satisfies the declaration) or it might not. Similarly, a method returning a List might or might not return an ArrayList.

Here was the compiler output:

javac Generics2.java -Xlint:unchecked

Generics2.java:12: warning: [unchecked] unchecked conversion found : java.util.List[] required: java.util.List[] someListArray = getArrayWhereEachElementIsALinkedListObject(); ^ Generics2.java:16: warning: [unchecked] unchecked conversion found : java.util.List[] required: java.util.List[] someListArray= getArrayWhereEachElementIsAListOfLinkedListObjects();
import java.util.ArrayList; import java.util.List; import java.util.LinkedList; public class Generics2 < public static void main(String[] args) < List[] someListArray; someListArray = getArrayWhereEachElementIsALinkedListObject(); // Why does this satisfy the declaration? //someListArray[0] => LinkedList object holding Strings someListArray= getArrayWhereEachElementIsAListOfLinkedListObjects(); //someListArray[0] => LinkedList object holding LinkedList objects > public static List[] getArrayWhereEachElementIsALinkedListObject() < List[] arrayOfLists = new LinkedList[2]; arrayOfLists[0] = getStringLinkedListAsList(); arrayOfLists[1] = getIntegerLinkedListAsList(); return arrayOfLists; >public static List[] getArrayWhereEachElementIsAListOfLinkedListObjects() < List list1 = new LinkedList(); list1.add(new LinkedList()); List list2 = new LinkedList(); list2.add(new LinkedList()); List[] arrayOfListsOfLinkedLists = new LinkedList[2]; arrayOfListsOfLinkedLists[0] = list1; arrayOfListsOfLinkedLists[1] = list2; return arrayOfListsOfLinkedLists; >public static List getStringLinkedListAsList() < List stringList= new LinkedList(); stringList.add("one"); stringList.add("two"); return stringList; >public static List getIntegerLinkedListAsList() < List intList= new LinkedList(); intList.add(new Integer(1)); intList.add(new Integer(2)); return intList; >>

Источник

How to create a type safe generic array in java?

I want to create a generic array in java maintaining the type safety usually offered by Java. I am using this code :

class Stack  < private T[] array = null; public Stack(ClasstClass, int size) < maximumSize = size; // type unsafe //array = (T[])new Object[maximumSize]; this.array = (T[])java.lang.reflect.Array.newInstance(tClass,maximumSize); >

In the above code (T[]) simply casts to Object[] , since the type T is unknown at compile time. Therefore array must be of type Object[] (or simply Object ). If you return a value typed T[] then, at the point of call, the compiler will throw in a hidden cast to the appropriate type, when the method’s return value is assigned or used.

Can you update your code so it compiles? Where does T come from? How is array declared? For example is this a static method you have mind (such that is just a wrapper around Array#newInstance ) or is array a field and T is a type parameter to a class?

4 Answers 4

The Array.newInstance(..) method has a return type of Object . As such, you cannot directly assign it to anything other than Object . You therefore need a cast.

The method delegates to a native method which

Creates a new array with the specified component type and length

Therefore it is creating an array of type T .

The type safety, assuming array is declared as

, is guaranteed by the Class parameter and the cast using the same type variable.

@SuppressWarnings("unchecked") 

with a comment explaining the above reason in your source code. Always comment why a cast whose warning you are suppressing is safe.

Note that the compiler could have guaranteed it if there was a newInstance specifically dedicated to reference-typed arrays. static T[] newInstance(Class c, int size)

Note that doing this will throw an exception for primitive types. For example int.class is a Class therefore casting an int[] into an Integer[] . There’s not a way to represent the primitives generically. That’s why it could be unsafe.

It’s not type safe because of the primitive Class objects. For example I can create a new Stack in the following manner:

Which is OK with the compiler but throws an exception because boolean.class is a Class and boolean[] cannot be cast to Boolean[] .

The alternative you show commented out:

Is actually somewhat type safe but for a different reason: it is due to erasure. You cannot, for example, cast a new Object[size] to a Number[] , but the cast never happens on the array. It happens some time later, like when you return an element of the array from a method (in which case the element is casted). If you tried to do something like return the array to outside the object it will throw an exception.

Usually the solution is not to generically type the array. Instead, do something like this:

The above cast is type safe because you can only push an E in to the array. Both the JDK Stack (which extends Vector) and ArrayList work this way.

If you want to use newInstance , you would have to reject primitives as there is no way to represent them generically:

Stack(Class tClass, int size) < if(tClass.isPrimitive()) throw new IllegalArgumentException(); // . >

Источник

Оцените статью