Why does this constructor that uses generics fail? - java

I am creating a class that, at present, stores lists of various types in an internal object called genericTable. Each list (composed of either Double, or Long) are all held in an object which is an instance of class GenericList.
Question: Why doesn't the method addVector work?
The error under the red underline says the constructor Test<V>.GenericList<V>(List<List<V>>) is undefined.
If I was working in a main method (but had the same GenericList class) and created genericTable within the main method (using List<GenericList<?>> Table = new ArrayList<GenericList<?>>();) and did genericTable.add(new GenericList<Long>(Arrays.asList(genericVector))); (where genericVector in this case is a List<Long>), it works perfectly.
public class Test<V> {
private final List<GenericList<?>> genericTable = new ArrayList<GenericList<?>>();
public void addVector(List<V> genericVector) {
genericTable.add(new GenericList<V>(Arrays.asList(genericVector)));
}
private class GenericList<K> {
private final List<K> listGeneric;
public GenericList(List<K> input) {
listGeneric = input;
}
}
}

You're unnecessarily using Arrays.asList(), when you already have a list. Consequently you get a list of lists, which is not what the constructr accepts.
See this from the javadocs:
This method also provides a convenient way to create a fixed-size list
initialized to contain several elements:
List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
So in your case you're getting a list of lists, instead of a list of strings.
I've added this bit from the comments, for clarity:
The method signature for asList() is like this:-
public static <T> List<T> asList(T... a)
So because T... a is a vararg, when you pass in "Larry", "Moe", "Curly", the compiled method actually receives an array of ["Larry", "Moe", "Curly"], and returns them as a List.
So because you passed in a List, rather than an array, the method takes the vararg array like this: [genericVector], and returns that array as a list, and you constructor breaks.

Related

Java Collection Type Parameter to array

I want to create a helper method which gets Collection type parameter to return a list. This is what I have now:
public class Helper {
public static <T> T[] CollectionToArray(Collection<T> collection) {
return collection.stream().toArray(Object[]::new); // Error
}
public static <T> T[] ListToArray(List<T> list) {
return list.stream().toArray(Object[]::new); // Error
}
}
public class IamSoNoob {
public void PleaseHelpMe() {
List<String> list = new ArrayList<>();
Set<String> set = new HashSet<>();
String[] arrayFromList = Helper.CollectionToArray(list); // Error
String[] arrayFromSet = Helper.CollectionToArray(set); // Error
String[] array = Helper.ListToArray(list); // Error
}
}
My questions are:
Is it possible to complete CollectionToArray(Collection<T>)?
If so, how?
Also, is it possible to pass List and Set as a parameter in the first place?
Is it possible to complete ListToArray(List<T> list)?
If so, how?
But here are some restrictions due to my personal taste.
I don't want to use #SuppressWarnings
I really want to keep the part .stream().toArray(Object[]::new) (Java 8 part!)
And I have a feeling that I need to fix the part Object[]::new by using something like: <T extends Object> or <? extends T> but I can't really figure out.
Please help me out, and please provide an explanation as well, I am often confused by Generic and ?.
No, you absolutely cannot do it, if it were possible the library method Collection.toArray() would've given you the same type as your LHS but instead when you want the exact type as your LHS you have to use Collection.toArray(T[]) (even that comes with ArrayStoreExceptions i.e it is up to the programmer to provide the right type for the array), the reason being that in your toArray() you've specified Object[] to be your array and later you cannot cast it to any other type or else it will result in a ClassCastException.
The reason for all this hullabaloo is because of the way generics works i.e its a compile time thing and at runtime Java erases all type parameters to their upper bound types and hence losing type information which is required for creating arrays.
One safe way of doing it is by adding another paramter to you helper method as
public static <T> T[] CollectionToArray(Collection<T> collection, T[] arr) {
return collection.stream().toArray(value ->Arrays.copyOf(arr,collection.size()));
}
and using it as
String[] arrayFromList = Helper.CollectionToArray(list, new String[0]);
but then everybody's better off using
Collection#toArray(T[]).

Generic array cast exception

I've got this Generic class, with a method returning a Generic Array:
public class ZTagField<T> extends JTextPane {
public ZTagField(StringBasedFactory<T> factory) {
assert (factory != null);
this.factory = factory;
init();
}
public T[] getItems() {
...
T[] arrItems = (T[]) currentItems.toArray((T[])new Object[0]);
return arrItems;
}
And another one using it:
public class Xxx {
ZTagField<clTag> txtTags = null;
public Xxx() {
txtTags = new ZTagField<clTag>(createFactory());
}
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
}
This latter txtTags.getItems()gives me an exception : ==> Exception [Object cannot be cast to [clTag ????
Can anyone explain me why ?
I've trie to apply as much of this How to create a generic array, to no avail.
I've got an ugly workaround :
return Arrays.asList(txtTags.getItems()).toArray(new clTag[0])
But I'd like to have it in then ZTagFieldClass.
Works as designed: at runtime there is no generic type.
It gets erased, and an array of Object is created. An array of Object can not be cast to another kind of array.
This is one of the restrictions of Java generics, based on the way how they are implemented.
That is way you are advised to be careful using arrays together with generics. You might prefer to use a generic List instead.
And just to be clear about this: yes, you can get arrays to work with generics, as elegantly shown by the other answers. But: you spend your time fighting symptoms doing so. Arrays and generics don't go together nicely in Java. Accept that, use generic lists and thus: fix the problem instead of working around it.
Arrays are reified. That means that they hold a reference to their component type, and when inserting an element, it uses that reference to check if the inserted element is actually a subtype of the component type.
Because of this, to create a T[], you need a concrete reference to the component type T, and since generics are erased, the generic T doesn't count. (That's also why you can't straight up create a T[] like T[] arr = new T[].)
The toArray method of a collection gets around this by having the user pass an array of the component type. But you try to cheat this by casting your Object[] to a T[], which doesn't actually create an array of T (where would the reference to the concrete T come from?). Such a cast would fail if it weren't unchecked, unless T was actually Object.
That's also where the ClassCastException comes from. You create an Object[], so the component type is Object, no matter if you cast it to T[], the component type stays Object. But later on, you know the actual component type you want (clTag):
public clTag[] getSelectedTags() {
return txtTags.getItems();
}
So the compiler will insert an implicit cast here to clTag[]:
public clTag[] getSelectedTags() {
return (clTag[]) txtTags.getItems();
}
But you can not cast an Object[] to a clTag[], just like you can not cast an Object to clTag.
Your workaround works, because you're actually supplying a reference to the component type:
Arrays.asList(txtTags.getItems()).toArray(new clTag[0]) // <-- 'clTag' here
A more modern solution than passing an array of the component type is to pass a IntFuntion<T[]>, which encapsulates an array constructor, to the method:
public T[] getItems(IntFunction<T[]> arrCons) {
...
T[] arrItems = currentItems.toArray(arrCons.apply(0));
return arrItems;
}
...
txtTags.getItems(clTag[]::new);
But you can't get around having to pass the component type in some way or another, unless you switch to returning a List<T> (as GhostCat also suggested). Since generics are not reified, you can create a List<T> without a reference to a component type:
public List<T> getItems() {
...
return new ArrayList<>(currentItems);
}
After compilation, the types are erased.
Since T is not bounded to a specific type, T will be replaced by Object.
So, this :
T[] arrItems = (T[]) currentItems.toArray((T[])...);
return arrItems;
will not create and return an array of the specific type used by the instance of the class at runtime but will only create an array of Object.
Besides, in Collection.toArray() you cannot pass either an array (new T[]) because it is not valid to create a generic array.
Consequently, if you want to use the toArray() method, you can finally only pass an array of Object in this way :
Object[] arrayObject = values.toArray(new Object[currentItems.size()]);
But an array doesn't work as a List type.
An array of a specific type cannot be cast to an array of another type even if the elements that it contains are of the type of the target of the cast.
So, you cannot cast an array of Object to an array of a specific type even if the array contains elements with this specific type such as.
So this will produce a ClassCastException :
clTag[] values = (clTag[]) arrayObject;
To solve your problem :
If you can use Java 8, using a functional interface is really a clean solution.
Jorn Vernee has given a very good answer illustrating it.
Otherwise, before Java 8, the single way to create an array of the same type that the parameterized type used in a generic collection is :
1) Creating a new array with the specified type.
The java.lang.reflect.Array.newInstance(Class clazz, int length) method
allows to create an array of the specified class and length.
2) Storing the class of the declared type in the instance of the generic class. You can do it by adding a class parameter in the constructor of it.
3) Populating it from the elements of the generic collection.
An easy way is using <Object, Object> Object[] java.util.Arrays.copyOf(Object[] original, int newLength, Class<? extends Object[]> newType) method but it is not effective as first you have to convert the collection into an array with toArray() to be able to pass it to the copyOf() method.
For example with a generic List, you could write :
public class ZTagField<T> {
private class<T> clazz;
private List<T> list = new ArrayList<>();
public ZTagField (class<T> clazz){
this.clazz = clazz;
}
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
Class<? extends Object[]> clazzArray = array.getClass();
array = (T[]) Arrays.copyOf(values.toArray(), values.size(), clazzArray);
return array;
}
}
It works but as said it is not effective.
A more effective solution would be iterating on the list and adding elements in the new array instead of using Arrays.copyOf():
public T[] get() {
T[] array = (T[]) Array.newInstance(clazz, list.size());
for (int i = 0; i < values.size(); i++) {
array[i] = values.get(i);
}
return array;
}

Different Java behavior for Generics and Arrays

Why does java allows inconsistent type to be entered into a generic object reference but not in an array?
For Eg:
When initializing array:
int[] a = {1, 2, 3};
And, if I enter:
int[] a = {1, 2, "3"}; //Error for incompatible types
While for generics,
import java.util.ArrayList;
public class Test {
private static ArrayList tricky(ArrayList list) {
list.add(12345);
return list;
}
public static void main(String[] args) {
int i = 0;
ArrayList<String> list = new ArrayList<>();
list.add("String is King");
Test.tricky(list);
}
}
The above code will let you add any Type in the list object, resulting in a run time exception in some cases.
Why is there such a behavior?? Kindly give a proper explanation.
When you use the tricky method to insert data into your ArrayList Collection, it doesn't match the specified type i.e String, but still This is compatible because of Generics compatibility with older Legacy codes.
If it wouldn't have been for this i.e if it would have been the same way as of arrays, then all of the pre-java generic code would have been broken and all the codes would have to be re-written.
Remember one thing for generics, All your type-specifications are compile time restrictions, so when you use the tricky method to insert data in your list reference, what happens is the compiler thinks of it as a list to which ANYTHING apart from primitives can be added.
Only if you would have written this:
...
public class Test {
private static ArrayList tricky(ArrayList<String> list) {
list.add(12345); //Error, couldn't add Integer to String
return list;
}
...
}
I have written a documented post on this, Read here.
The method's parameter has no generic so all classes are allowed.
You may google 'type erasure' for more information.
If you add the generic type to your method you will get a compiler error:
private static ArrayList<String> tricky(ArrayList<String> list) { // ...
By the way, you do not need to return the list because you modify the same instance.
Here's why:
The reason you can get away with compiling this for arrays is because
there is a runtime exception (ArrayStoreException) that will prevent
you from putting the wrong type of object into an array. If you send a
Dog array into the method that takes an Animal array, and you add only
Dogs (including Dog subtypes, of course) into the array now referenced
by Animal, no problem. But if you DO try to add a Cat to the object
that is actually a Dog array, you'll get the exception. Generic
Methods (Exam Objectives 6.3 and 6.4) 615 616 Chapter 7: Generics and
Collections
But there IS no equivalent exception for generics, because
of type erasure! In other words, at runtime the JVM KNOWS the type of
arrays, but does NOT know the type of a collection. All the generic
type information is removed during compilation, so by the time it gets
to the JVM, there is simply no way to recognize the disaster of
putting a Cat into an ArrayList and vice versa (and it becomes
exactly like the problems you have when you use legacy, non-type safe
code)
Courtesy : SCJP Study guide by Kathy Sierra and Bert Bates
When you declare you ArrayList like ArrayList list = ... you do not declare the type of object your list will contain. By default, since every type has Object as superclass, it is an ArrayList<Object>.
For good practices, you should declare the type of your ArrayList<SomeType> and, thereby, avoid adding inconsistant elements (according to the type)
Because you haven't defined the generic type of your list it defaults to List<Object> which accepts anything that extends Object.
Thanks to auto-boxing a primitive int is converted to an Integer, which extends Object, when it is added to your list.
Your array only allows int's, so String's are not allowed.
This is because in your method parameter you did not specify a particular type for ArrayList so by default it can accept all type of objects.
import java.util.ArrayList;
public class Test {
//Specify which type of objects you want to store in Arraylist
private static ArrayList tricky(ArrayList<String> list) {
list.add(12345); //This will give compile time error now
return list;
}
public static void main(String[] args) {
int i = 0;
ArrayList<String> list = new ArrayList();
list.add("String is King");
Test.tricky(list);
}
}

Regarding method overloading in java [duplicate]

This question already has answers here:
Overloaded method selection based on the parameter's real type
(7 answers)
Closed 10 years ago.
I was going through the method overloading in java and I was trying the output of the below program in eclipse , the program is ..
public class OverloadingTest {
public static void main(String args[]){
List abc = new ArrayList();
List bcd = new LinkedList();
ConfusingOverloading co = new ConfusingOverloading();
co.hasDuplicates(abc); //should call to ArryList overloaded method
co.hasDuplicates(bcd); //should call to LinkedList overloaded method
}
}
class ConfusingOverloading{
public boolean hasDuplicates (List collection){
System.out.println("overloaded method with Type List ");
return true;
}
public boolean hasDuplicates (ArrayList collection){
System.out.println("overloaded method with Type ArrayList ");
return true;
}
public boolean hasDuplicates (LinkedList collection){
System.out.println("overloaded method with Type LinkedList ");
return true;
}
}
and the output is ..
Output
overloaded method with Type List
overloaded method with Type List
Now in the explanation it was told ..method overloading is resolved at compile time using static binding in Java, so please advise how can I achieve the same through method overriding.
abc,bcd both are of type List even when you initialize it with a subclass.hence the result
A BaseClass like List helps you to write methods that can work with any of it's subclass(ArrayList or LinkedList).
So,
public ArrayListLinkedListCanCallMe(List lst)
{
//now imagine if this method was called with bcd as parameter
//still lst would be of type List not LinkedList
//and if lst were allowed to be of type LinkedList then how could List know any
//of the methods of LinkedList.Therefore lst would always be of type List NOT LinkedList
}
You can instead try
co.hasDuplicates((ArrayList)abc);
co.hasDuplicates((LinkedList)bcd);
But (ArrayList)abc can throw cast exception if abc is of type LinkedList.You can use instanceof operator to check if abc is of type ArrayList and then cast it..
In this particular case, if hasDuplicates means what it says, I would have one method taking List as argument. It would just create a HashSet initialized with the List contents and compare its size to the List size.
If you really do need special case code for e.g. ArrayList you could use instanceof inside a List argument method.
However, when you are working with an interface type it is much, much better to find common approaches that will work for all implementations of the interface. If you cannot do that, you must allow for the possibility of being passed an object of a class that implements the interface but is not one of the classes for which you have special case code. If you need special case code for java.util.ArrayList, why don't you need special case code for instances of the private class that Arrays.asList uses for its result?
When the issue is different classes in your own code, you can often turn the problem around and put the method in what is currently the argument class, so that conventional overriding works.

Is there a parameter I can use in Java that works with all for-each loops?

Suppose I've got a method that accepts an array and processes each element in it using Java's built in for-each loop, like this:
public static void myFun(SomeClass[] arr) {
for (SomeClass sc : arr) {
// Stuff is processed here
}
}
This works just fine, but now I want to be able to pass the same method a List<SomeClass> instead. Am I destined to use Collection.toArray(T []), or is there a parameter I can use for myFun() that accepts any type that can be used in a for-each construct?
To clarify: I want a method signature that will accept any iterable object, be it a primitive array or a Collection. I can very easily write two methods, with one wrapping the other, but I'm just curious if there's a better way.
I would suggest using Iterable, Collection or List as the parameter type.
IMO, collections should be preferred to reference arrays. If you happen to have an array Arrays.asList does the conversion nicely. Arrays.asList allows gets and sets back through to the array, but obviously not "structural" modifications which would change the array length.
myFun(Arrays.asList(arr));
You may have to use wildcards in extreme/general cases.
public static void myFun(Iterable<? extends SomeClass> somethings) {
for (SomeClass something : somethings) {
// something is processed here
}
}
It is noteworthy that Collections.toArray and Arrays.asList work slightly differently. asList keeps the original array to back the collection, so changes to the collection will be reflected in the array. Collections.toArray makes a (shallow) copy of the collection data. Making a copy is often what you would want anyway if you are returning an array. Asymmetrically, if you are passing as an argument you generally do not copy (unless storing as a field).
Use Iterable. That's what it's for.
As you said, Iterable won't handle arrays.
You don't want to use multiple methods wrapping each other. That rules out Arrays.asList and Collection.toArray.
So the answer to your question is no, there isn't a way. But if you can use Lists, why would you ever use arrays?
I would still go with Iterable here. I like it better than Collection because in the past I've had classes that implemented Iterable but were not collections; this made it easy for them to lazily retrieve values as needed, and I could use the foreach loop on them.
you cannot, java Arrays doesn't implements Iterable:
public static int sum(Iterable<Integer> elements) {
int s = 0;
for (int i : elements) {
s += i;
}
return s;
}
public static void main(String[] args) {
L1: System.out.println(sum(1,2,3));
L2: System.out.println(sum(Arrays.asList(1,2,3)));
L3: System.out.println(sum(new int[] { 1,2,3 }));
}
this results in two compile-time errors in (L1 and L3); so you must design your
method to accept an Iterable (Collections) and/or an Array, at least one method must perform some conversion (to/from array)
WORKAROUND:
you may be try with an adapter:
public class ArrayIterator<T> implements Iterator<T> {
private final T[] array;
private int i;
public ArrayIterator(T[] anArray) {
array = anArray;
i = 0;
}
public boolean hasNext() {
return i < array.length;
}
public T next() {
return array[i++];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static int sum(final Integer ... elements) {
return sum(new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new ArrayIterator<Integer>(elements);
}
});
}
you should pay attention only when dealing with primitive arrays; when you use only
reference object (your case) ArrayIterator + anonymous class are cool
hope it helps
Short answer: no, there's no single method signature that type-safely accepts both an Iterable and an array. Obviously you could just accept Object, but that would be a hack as well.
Long-ish answer: Since the enhanced for-loop is effectively defined twice (once for arrays and once for Iterable), you'll need to provide two overloaded methods as well:
public static void myFun(SomeClass[] array) {
for (SomeClass sc : array) {
doTheProcessing(sc);
}
}
public static void myFun(Iterable<? extends SomeClass> iterable) {
for (SomeClass sc : iterable) {
doTheProcessing(sc);
}
}
Although the source of the two methods looks exactly the same, you'll need to define it twice (unless of course you wrap the Array in your own Iterable as #dfa outlined).
There's a little know feature of Java Generics in Java 1.5+ where you can use <? extends Subtype> in your method calls and constructors. You could use <? extends Object>, and then anything that deals with those would have access only to methods on Object. What you might really want is something more like this:
List<? extends MyCrustaceans> seaLife = new ArrayList<? extends MyCrustaceans>();
MyShrimp s = new MyShrimp("bubba");
seaLife.add(s);
DoStuff(seaLife);
...
public static void DoStuff(List<? extends MyCrustaceans> seaLife)
{
for (MyCrustaceans c : seaLife) {
System.out.println(c);
}
}
So if you have a base class (like MyCrustaceans), you can use any methods of that base class in DoStuff (or of the Object class, if your parameter is <? extends Object>). There's a similar feature of <? super MyType>, where it accepts a parameter that is a supertype of the given type, instead of a subtype. There's some restrictions on what you can use "extends" and "super" for in this fashion. Here's a good place to find more info.
public static void myFun(Collection<MyClass> collection) {
for (MyClass mc : collection) {
// Stuff is processed here
}
}

Categories

Resources