Java generic object with varying types. - java

Imagine having 2 Generic ArrayLists each storing different types. My program will be using only one of these ArrayLists at a time. Is it possible to create a general ArrayList (currentArrayList) which can store both ArrayLists, and use the two ArrayLists without casting.
ArrayList<Integer> arrInt = new ArrayList<>();
arrInt.add(10);
ArrayList<String> arrString = new ArrayList<>();
arrString.add("ten");
ArrayList<XXX> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
currentArrayList = arrString;
String str = currentArrayList.get(0);
Thanks.

The problem is the part where you want to:
use the two ArrayLists without casting.
Definitely not going to happen. The whole point of a static type system is to stop you from being able to treat a general type as a specific type without explicitly asking to do so.
So for example, you could say:
ArrayList<?> currentArrayList = arrInt;
Object i = currentArrayList.get(0);
But notice that i is just an Object, so the compiler is happy - we don't know anything about the things in the array except that they must be Object-based because this is a Java program. But you want to say:
ArrayList<?> currentArrayList = arrInt;
Integer i = currentArrayList.get(0);
It's that second line that isn't ever going to happen without a cast.
UPDATE Question from comments:
Can't the compiler easily infer the type of the ArrayList by
looking at the generic type of arrInt ?
Suppose it did that for us. What should it then do with the type of currentArrayList when it sees the line:
currentArrayList = arrString;
Your code as it stands assumes that the compiler is not going to do that kind of inference. Suppose you comment-out that second assignment. Now the compiler could make the inference you suggest, and allow your code to compile. But then in the future if you put back the second assignment, the rest of your code would stop compiling! This would be needlessly confusing.

You have to determine the type of the object anyway eventually.
i guess you want to avoid "supresswarning("casting")" kind of warning?
if ture, here it goes:
ArrayList<Object> list = new ArrayList<Object>();
list.add(1);
list.add("abc");
list.add(new Double(3.1415926));
list.add(true);
for (Object o : list) {
// or more accurate
String type;
if (o instanceof Integer) {
type = "int";
// logic here...
int i = (Integer) o; // no casting warning
} else if (o instanceof String) {
type = "string";
// logic here...
}
// other possibilities if you want
else { // unmatched
type = o.getClass().getSimpleName();
}
System.out.println(type + ": " + o);
}

Misunderstood the question - why on earth would you want to do that?
You are not creating a new ArrayList you are just using a Pointer without the type - its still the same ArrayList!
ArrayList<ArrayList<Object>> array = new ArrayList<ArrayList<Object>>();
array.add(new ArrayList<Integer>());
array.add(new ArrayList<String>());
This should do the trick, since all other possible Objects will inherit from Object in hence be accepted.

Related

Generics and Collections, don`t understand

Is there any difference between these 3 lines?
List list2 = new ArrayList<String>();
List list2 = new ArrayList<>();
List list2 = new ArrayList();
As I understand, all information about generics is erased in runtime. Consequently, only type of veriable is important. So the above lines of code mean the same thing to me. If I am wrong can anybody give me some exapmle that shows the difference?
p.s. sorry for my english
While it is true that generic data is erased at compile-time, that doesn't mean they are totally useless. The only line that would compile in your example is the last one, but you would get a warning that you are using RawTypes. A RawType is a generic class that does not have a generic object. The first two lines are only half-built.
The reason you pretty much have to use generics is that the list is type-safe. If you use generics, you could use something like this:
List<String> list = new ArrayList<>();
String out = list.get(0);
If you use raw types, you would have to do this:
List list = new ArrayList();
String out = (String) list.get(0);
This may seem OK, but what if you add a value to the list that is not a String? The program crashes. For example:
List unsafe = new ArrayList();
List<String> safe = new ArrayList<>();
unsafe.add("hi");
unsafe.add(new Tree());
safe.add("hi");
safe.add(new Tree()); // This line would throw an exception.
String out = (String) unsafe.get(0);
String out1 = safe.get(0);
String out2 = (String) unsafe.get(1); // This line would throw an exception.
If you still don't quite see why to use generic types, the final nail in the coffin for raw types is this: If my (String) unsafe.get(1) is in a separate class, on the hundredth line, I know that that line is the problematic line. I, however, don't know where the non-string object is being added, only where it's being accessed.
If you use generic types, you know exactly where the problematic addition is made, and you can prevent it.

Using an Array as table in custom generic Hashtable to store entries

I am experimenting with making my own custom Hashtable as a way of understanding the data structure, and have run into what many other people seem to have run into; that you cannot create a generic array the same way you would make another array. I understand the reasons for this, but also know that Java's HashMap itself uses an array to store Entry items. If my understanding is correct, Java's HashMap creates an object[] and then casts each element of the object to the Entry class each time the put or get method is called. Is this correct?
I have read answers about generic arrays saying it is possible to instead do something like having Entry[] table as a class variable and then using table = (Entry[]) new Object[size]; in the constructor as a way of avoiding having to do the casting in both the put and get methods, but this does leads to a ClassCastException, which is understandable since it would have to check each element of the Object array anyway in order to make sure they are the Entry class. Does this mean I cannot use this method in my custom Hashtable?
Finally, another way of creating the Entry array seems to be checking the class type in the constructor and using Entry[] table = (Entry[]) Array.newInstance(c, s); Is this method perhaps more suitable?
Below is a snippet of my own code relevant to this question. I am wondering if my interpretation of everything above is correct, and if this is an acceptable way of going about my own Hashtable. I also understand my method of using determining the index for the given hashCode may be incorrect, but that is outside the scope of my question :), and my put and get methods are definitely incomplete!
public class HashTable<K, V> {
Object[] buckets;
HashTable(int size) {
buckets = new Object[size];
this.size = size;
}
void put(K key, V value) {
int i = key.hashCode()%size;
buckets[i] = (Entry) new Entry(key, value, (Entry) buckets[i]);
}
K get(K key) {
int i = key.hashCode()%size;
Entry entry = (Entry) buckets[i];
return entry.key;
}
}
If my understanding is correct, Java's HashMap
creates an object[] and then casts each element of the object to the
Entry class each time the put or get method is called. Is this
correct?
The standard library's source is available. You could check it for yourself. If you did, you would find that no, that's not quite what java.util.HashMap does.
I have read answers about generic arrays saying it is possible to
instead do something like having Entry[] table as a class variable and
then using table = (Entry[]) new Object[size];
To the extent that such answers recommended exactly what you describe, they are wrong. I suspect, however, that your "something like" does not capture the key elements of the answers you saw.
There are two potential issues
Creating an array whose element type is drawn from a type parameter:
class MyClass<T> {
// CAN'T DO THIS:
T[] array = new T[2];
// can do this:
T[] array = (T[]) new Object[2];
// or this:
Object[] array = new Object[2]; // (and cast later)
}
Creating an array whose element type is parameterized
class MyOtherClass<T> {
// CAN'T DO THIS, EITHER:
SomeType<T>[] array = new SomeType<T>[2];
// can do this:
SomeType<T>[] array = (SomeType<T>) new SomeType[2];
// or this:
SomeType[] array = new SomeType[2]; // (and cast later)
}
As you will have seen in the JDK source (you did follow the above link, right?), HashMap's issue is of the second type, and what it does is create an array of the appropriate raw type, and then cast that to the desired parameterized type -- which will trip the compiler's type safety warnings, but is in fact perfectly type safe as long as no other, raw or differently parameterized, reference escapes.
in the constructor as a
way of avoiding having to do the casting in both the put and get
methods, but this does leads to a ClassCastException [...]. Does this
mean I cannot use this method in my custom Hashtable?
Yes, of course it does. The method you describe and demonstrate is invalid, as the exception tells you. An Object[] is not an Entry[]. But that's not what the answers you reviewed were suggesting you do.
Finally, another way of creating the Entry array seems to be checking
the class type in the constructor and using Entry[] table = (Entry[])
Array.newInstance(c, s); Is this method perhaps more suitable?
Rarely is reflection a better answer for anything. It only makes sense when you don't have all the type information you need at compile time, and that is not your case.
It looks like your Entry class is an inner class, which I'd recommend against because it makes things more complicated. First, let's just assume that we don't have an inner class.
For the illustration, we have a simple generic class:
class Foo<T> {}
There's a difference between these two generic array types:
class Container<T> {
// creating an array with erasure of T[]
// vvvvvvvvvvvvv
T[] arrA = (T[]) new Object[N];
// creating an array with erasure of Foo<T>[]
// vvvvvvvvvv
Foo<T>[] arrB = (Foo<T>[]) new Foo[N];
// Note that the following would be slightly
// better because it doesn't use a raw type,
// but it doesn't work for this illustration
// because it's not the erasure of Foo[]:
// (Foo<T>[]) new Foo<?>[N];
}
Casting checks the erasure of type, so suppose we create a new container and assign those arrays to something in the outside world:
Container<String> c = new Container<String>();
String[] arrA1 = c.arrA;
Foo<String>[] arrB1 = c.arrB;
// After erasure these assignments become:
String[] arrA1 = (String[]) arrA;
Foo[] arrB1 = arrB;
The first assignment, arrA1 = c.arrA throws a ClassCastException, but the second assignment, arrB1 = c.arrB does not. This is because in the first case the conversion is from Object[] to String[] whereas in the second case there is no checked cast because all parameterizations of Foo<T> just become Foo after erasure.
This is all to explain my next point which is that creating an array of a parameterized type is more acceptable than creating an array of a type variable. In the case of the type variable array we have an Object[] masquerading as a T[] but in the case of the parameterized type we actually do have an array of Foo[], it's just that there is no checking for the type arguments to Foo. In other words:
Container<String> c = new Container<String>();
// Recall that this assignment doesn't throw a ClassCastException
Foo<String> arrB = c.arrB;
Object[] arrBAsOBj = arrB;
// This assignment throws an ArrayStoreException
arrBAsObj[0] = new StringBuilder();
// This assignment does not throw an ArrayStoreException
arrBAsObj[0] = new Foo<Integer>();
Although, I'd like to note that you should never expose a generic array to the outside world. I'm just doing that to illustrate the explanation.
Anyway, if you're writing something like a hash table, it's acceptable to create an unchecked array of a parameterized type. I usually write a helper method like this:
private static <K, V> Map.Entry<K, V>[] createUncheckedArray(int length) {
#SuppressWarnings("unchecked")
final Map.Entry<K, V>[] unchecked =
(Map.Entry<K, V>[]) new Map.Entry<?, ?>[length];
return unchecked;
}
Just don't return it to the outside world, because we still don't actually have a generic array, just an array of Map.Entry with unchecked type arguments.
Really Java should just have a simple class like Array<T> for this sort of case when we actually need a fixed-length container.
For an inner class you have to use a parameterized type as a qualifier, something like this:
private Entry[] createUncheckedArray(int length) {
#SuppressWarnings("unchecked")
final Entry[] unchecked =
(Entry[]) new HashTable<?, ?>.Entry[length];
return unchecked;
}

Array of generic holders

If I have a map of Entry .. objects , and I have an array in a class
private Entry<K,V> array;
Can I say
array = new Entry[someInt];
which I've done, or do I need a typecast like my instructor says is necessary such as
array = (Entry<K,V> E[]) new Entry[someInt];
Note that the first one did work when I ran my JUnits.
Have you thought about doing a List ?
Entry<K,V> array = new ArrayList<Entry<K,V>>();
array.add(new HashMap<K,V>());
You can do either one. Implicit conversions to and from raw types are allowed without a cast.
array = new Entry[someInt]; will produce an unchecked conversion warning.
array = (Entry<K,V>[])new Entry[someInt]; will produce an unchecked cast warning.
So neither one is really better than the other.
Note that if you create the array with the wildcard type, you will need to have a cast:
array = (Entry<K,V>[])new Entry<?,?>[someInt]; // compiles
array = new Entry<?,?>[someInt]; // doesn't compile

Why is an array of an unknown generic type allowed in Java?

In the Java tutorials ( http://docs.oracle.com/javase/tutorial/extra/generics/fineprint.html ) I saw the following:
// Not really allowed.
List<String>[] lsa = new List<String>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Unsound, but passes run time store check
oa[1] = li;
// Run-time error: ClassCastException.
String s = lsa[1].get(0);
If arrays of parameterized type were allowed, the previous example
would compile without any unchecked warnings, and yet fail at run-time
// OK, array of unbounded wildcard type.
List<?>[] lsa = new List<?>[10];
Object o = lsa;
Object[] oa = (Object[]) o;
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Correct.
oa[1] = li;
// Run time error, but cast is explicit.
String s = (String) lsa[1].get(0);
They then explained that if we switch List<String>[] lsa = new List<String>[10]; to
List<?>[] lsa = new List<?>[10]; then it's okay but we have to upcast.
A proffessor of mine asked the following about this: "Why does the latter compile?"
He then gave the answer: "When the argument is ? the meaning is that every cell in the array can include an ArrayList. Because there aren't any assumptions about the type of the generic class the previous exception can't happen."
It still doesn't really make sense to me that the the wildcard one works while the previous one doesn't. If we had to enforce upcasting in the wildcard example why couldn't we do it in the first example as well?
I'd appreciate if someone could clear this up for me.
The wild-card one forces you to cast, so it's up to you to know the real class.
The first case (if possible) would give you a false sense of security and no warnings, since the whole point of generics is to allow you to work without constantly casting things.
Generics were designed to be type-safe in case you provide a certain type. But due to type erasure, the information of which type exactly can be stored in your generic list is lost. This would have the same effect as if you would just provide ? as the type. At runtime it is not sure that the types will always be correct and thus the compiler complains.
The data in both variations will be the same but one variation compiles, the other does not. This is because when providing the wildcard, you basically turn off the type-safety of the compiler like a switch and tell it that you will care about it yourself. If it will fail, it will be your own fault, and not the compilers fault anymore. :)
The actual reason that the first one is disallowed is because java's type system is unsound.
The spec says that if S is-a T, then S[] is-a T[]. Unfortunately this rule causes type problems, to wit:
void unsoundness( Animal[] aanl ) {
// causes a runtime type check that the element types are compatible
aanl[ 0 ] = new Animal();
}
Dog[] adog = new Dog[ 1 ];
unsoundness( adog );
With generics and type erasure, that runtime type check can't make the correct determination.
void unsoundness( List< ? extends Animal >[] alstr ) {
alstr[ 0 ] = new ArrayList< Animal >();
}
List< Dog >[] alobj = new List< Dog >[ 1 ]; // fictitious
unsoundness( alobj );

What Benefit Generics provide over legacy Code:

i have read on docs.oracle site that The following code snippet without generics requires casting:
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);
but if i write code with Generics then it is still prone to Error:
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
what is then the real use of generics....:( thnx in advance..
List<Object>= new List<Object>;
list.add("hello");
String s=(String)list.get(0);
Should be
List<String>= new ArrayList<String>(); // this is now a list of String, not a list of object
^^^^^^ ^^^^^^
list.add("hello");
String s=list.get(0); // no casting needed
^
You parameterize by the type you want. Your example are 2 ways to do the same thing, since you parameterize by the most basic class.
The advantage of generics is that you can write classes that are more specific to one class, String here. This gives you better type safety to catch bugs early during compilation. This prevents issues arising from the casting approach.
Using generics makes your code Type Safe. You can prevent ClassCastException.
Suppose you want to store a list of names(string)
List listNames = new ArrayList();
listNames.add("Durgesh");//ok
But I could also add an integer to it
listNames.add(5000);//storing int instead of string
Now do this
String name2=listNames.get(1);//throws exception{int->string}
Without generics you could add invalid types to collection which could break your code.
With generics you could solve the problem
List<String> listNames = new ArrayList();
listNames.add("Durgesh");
listNames.add(3000);//would through error at compile time
So,generics provides typesafety
With List<Object> you intend to add any kind of Object.Due to Object parameter,it would allow you to add any kind of object(string,int).
Also List<x> cannot be assinged(=) to List<y> or vice versa if x can be converted to y or y can be converted to x..They both should be x or y thus providing type safety
So,you wont be able to assign(=) List<String> to List<Object> or vice versa..
Generics are used to detect runtime exceptions at compile-time itself.
Assume that you created a List to store Strings and passed it to a method.. enhanceList(List).. and after the execution, you will iterate through the list and get all strings
before genercis, it could have been possible that enhanceList(List) method will add other type of objects into the list creating possible ClassCastException
void someMethod() {
List listOfStrings = new List();
enhanceList(listOfStrings);
for(Iterator i : listOfStrings.iterator(); i.hasNext();) {
String s = (String) i.next(); //RuntimeException here
}
}
void enhanceList(List l) {
l.add(new Integer(1)); //error code
}
with generics, you can very well "bind" the type of objects the list contains
void someMethod() {
List<String> listOfStrings = new List<String>();
enhanceList(listOfStrings);
for(String s : listOfStrings) {
//no error here
}
}
void enhanceList(List<String> l) {
l.add(new Integer(1)); //compile-time error
}
However, generics should be used with caution, List<Object> doesn't help much with binding types because, it can hold any objects (since Object is super class of all the java classes). I recommend to create List of Specific type always.

Categories

Resources