How to write a generic function which accepts arrays of literals - java

I am unable to provide values of type int[], float[], etc. to a generic function. I get errors that say basically that float[] is the wrong type and Float[] is what the function actually takes.
Here's an example of a method I wrote, and I'm trying to give it values like new int[]{0,1} (created in library somewhere else).
private static <T> JSONArray encodeArray(T[] array) {
JSONArray arr = new JSONArray();
Collections.addAll(arr, array);
return arr;
}
Is it even possible to write my function signature to accept these arrays of literals?
I could go to the call site, and do a conversion of float[] to Float[], but I don't know how to do that either.

Is it even possible to write my function signature to accept these
arrays of literals?
It is possible, but the parameter type will have to be Object, because that is the only common superclass of "arrays of primitives" and "arrays of references" (e.g. it can't be Object[] since arrays of primitives are not subclasses of Object[]). (There are also some interfaces that all arrays implement, but I will ignore those for now.) Unfortunately, this means that you will lose type safety as the compiler will not be able to give an error at compile time if someone passes a non-array type in.
To do array operations on this Object value, you will need to use the methods in the reflection helper class java.lang.reflect.Array. So you can do something like this:
import java.lang.reflect.Array;
// ...
private static JSONArray encodeArray(Object array) {
JSONArray arr = new JSONArray();
for (int i = 0, n = Array.getLength(array); i < n; i++) {
arr.add(Array.get(array, i)); // primitives are automatically wrapped
}
return arr;
}

The method which accepts a generic array.
public <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
You can't use primitives in generic functions. When generic are compiled, you end up with Object[] in the above example as the implementing type. As int[] and byte[] etc, do not extend Object[] you cannot use them interchangeably even if the code involved would be identical (again generics are not templates)

Add on the solution from #Max-Reshetnyk, It is better if you check ArrayList for methods that help you add or remove... elements. Since primitive types are not meant to be used with generics, you should AutoBox them with their respective types and then use generics.
For instance:
class Main {
public static void main(String[] args) {
Integer[] x = new Integer[1];
x[0] = 1;
printArray(x);
}
public static <T> void printArray(T[] array){
for (T element: array){
System.out.println(element);
}
}
}

Related

Is it possible to return an array of type <E> in a method in Java rather than an array of objects when using Generics?

I have a method that takes an in an array and copies it in a random order into another array and returns the shuffled array.
However, if I want to make it generic, I can't create the second array of type E.
To get around this, I tried using an Arraylist and then using the .toArray() method and casting it to type E, but that returns an array of objects.
My current solution is to just modify the array directly and return that, but is there a way to return an array of the proper type, AKA the type of the array passed into the method?
import java.util.ArrayList;
import java.util.Arrays;
public class ShuffleArray
{
public static void main(String[] args)
{
String[] list = {"bob", "maryo", "john", "david", "harry"};
//doesn't work, can't store array of objects in array of strings
list = shuffle(list);
//works because I modify directly
shuffle(list);
}
public static <E> E[] shuffle(E[] list)
{
ArrayList<E> shuffledList = new ArrayList<>();
//shuffle the array
while (shuffledList.size() != list.length)
{
int randomIndex = (int)(Math.random() * list.length);
if (!shuffledList.contains(list[randomIndex]))
{
shuffledList.add(list[randomIndex]);
}
}
//overwrites the initial values of the array with the shuffled ones
for (int i = 0; i < list.length; i++)
{
list[i] = shuffledList.get(i);
}
//How do I make this return an array of type String?
return (E[]) shuffledList.toArray();
}
}
All arrays have a public clone() method, which returns the same type as the original array:
return shuffledList.toArray(list.clone());
You have a different problem, better described here: make arrayList.toArray() return more specific types
Change your return statement to the following
return shuffledList.toArray(E[]::new);
You could use Arrays.copyOf:
shuffledList.toArray(Arrays.copyOf(list, list.length));
although internally the method uses casting as well.
By the way, there is a built-in method Collections.shuffle. Maybe the best approach would be to work on Lists instead of raw arrays?
UPDATE: for the copyOf approach to work, you would need to bound the type parameter to Objects i.e. change it from <E> to <E extends Object>. The method wouldn't work for raw types (int, long etc.).
yeah why dont u just create an array of type E and store the values in it
E[] array = new E[list.length];
then just use that array to store the shuffled values and return it

How to populate generic array to main

I've got a generic array class and I want to return an array in the main so I can use the sort method that I have ready in the main. I understand that the constructor has an array in it so I'm wondering if I can use that. Or do I need to set up a new method to return this.array ? Also it returns a generic array, how do I choose the type in main?
public class dynamicArray <T>{
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
Here I chose the integer type for the class. I'm not sure how can I extract the
array from the constructor.
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int [] arr = array.populate();
mergeSort(arr);
System.out.println(array.toString());
}
Unfortunately, arrays and generics don't work well together. Take a look at the source code of java's ArrayList - it is implemented with an Object[] and not a T[] - then every method will cast to T (which costs literally zero, it's just ugly and causes compiler warnings). I advise you do the same here: Arrays actually KNOW their component type (unlike a list of Ts, which does not, there is no method on a java.util.List that you can invoke to get the component type), and therefore casting Object[] to T[] is just wrong; java allows this solely for backwards compatibility reasons.
Basically, you can't work with T[] without things being subtly wrong and a lot of compiler errors.
In this specific case? I would strenuously advise you to use a private List<T> array; field instead of a T[] field.
Your call to array.populate() (that seems like a bizarre name for this method!) IS retrieving the array you created in the constructor. You are doing what you're asking for: "Extracting the array from the constructor" - invoking populate() on the object returned by the new dynamicArray<Integer>() is doing exactly that.
NB: You have a typo in your source code. it's new dynamicArray<Integer>();, not new<Integer>dynamicArray();. Perhaps that's causing some issues?
NB2: Java conventions dictate it's DynamicArray, and something like getBackingArray (instead of populate).
I think you ask two question :
How to set Integer type of that array object.
How to get Integer[] to int[]
Here is the code :
private int index;
private T[] array;
public dynamicArray() {
array = (T[])new Object[10];
this.index = 0;
}
public T [] populate() {
return this.array;
}
public void add(T x) {
array[++index] = x;
}
public static void main(String[] args) {
dynamicArray<Integer>array = new<Integer>dynamicArray();
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
int[] arr = Arrays.stream(array.populate())
.mapToInt(i -> i)
.toArray();
System.out.println(array.toString());
}
Answer for 1st question is you can not set Integer type because there wasn't any add method in your class. Answer for 2nd question is you try to convert Integer[] to int[] but there is no direct way to cast this. you just need to change Integer -> Object then Object -> int. This can be done easily using streams which is in Java 8 and i have used lambda here for showing power of lambda function.
Here is a possible alternative. Pass the type of array to the constructor. But essentially you are creating a limited form of ArrayList so you may just as well use that. Note that this still has the limitation that you can't use primitive arrays as the array type.
dynamicArray<Integer> array = new dynamicArray<>(new Integer[0]);
array.add(10);
array.add(5);
array.add(6);
array.add(11);
array.add(13);
array.add(20);
Integer[] a = array.getArray();
System.out.println(Arrays.toString(a));
}
class dynamicArray<T> {
private int size = 0;
private T[] array;
public dynamicArray(T[] a) {
array = a;
}
public void add(T value) {
if (array.length == size) {
array = Arrays.copyOf(array, size == 0 ? 10 : size*2);
}
array[size++] = value;
}
#SuppressWarnings("unchecked")
public T[] getArray() {
// need to copy the array since the length and size could be different.
T[] arrayCopy = (T[]) Array.newInstance(array.getClass().getComponentType(), size);
System.arraycopy(array, 0, arrayCopy, 0, size);
return arrayCopy;
}
}

How do I create an instance of a class with two generic types?

I'm trying to create an instance of a class ZipIterator which takes two arbitrary arrays as arguments and sets them equal to two private fields K[] and V[] via the constructor. In my Testing class' main method, I'm writing
import java.util.Iterator;
public class ZipIterator<K,V>
{
private K[] arr1;
private V[] arr2;
private int pos = 0;
public ZipIterator(K[] arr1, V[] arr2) {
this.arr1 = arr1;
this.arr2 = arr2;
}
}
In my Testing class' main method, I'm trying to create a ZipIterator object like this
int[] arr1 = {1,5,3,1,6};
double[] arr2 = {2.3,42.1,1.6,6.43};
ZipIterator<int[],double[]> zip = new ZipIterator<int[],double[]>(arr1,arr2);
but I keep getting the error:
error: incompatible types: int[] cannot be converted to int[][]
I'm not exactly sure what I am doing wrong. If anybody can help, it would be greatly appreciated!
Think about the signature of ZipIterator<K[], V[]> when K is int[] and V is double[].
What you need to instantiate is ZipIterator<Integer, Double>; you cannot parametrize a class with a primitive type.
Yes, it's inefficient because of boxing/unboxing. If you want higher performance, make wrapper classes that keep an array of a primitive type as an instance variable. Will be tricky to make it nicely parametric, though.
Look closely what happens:
You are using int[] as generic type K:
ZipIterator<int[], double[]> zip = new ZipIterator<int[],double[]>(arr1, arr2);
Your constructor, however, is accepting an array of K, so an array of int[] would require int[][]:
public ZipIterator(K[] arr1, V[] arr2) { ... }
Notes:
You shouldn't use arrays with generics, use the List interface instead:
public ZipIterator(List<K> arr1, List<V> arr2) { ... }
Or, like 4castle mentioned, you can even use the Iterable interface.
Unlike what others said, an array is always an object, even if it is an array of a primitive type. So using int[] for K is perfectly valid, whilst int is not.
It should be
ZipIterator<int,double> zip = new ZipIterator<int,double>(arr1,arr2);
Use the wrapper classes
Integer[] arr1 = {1,5,3,1,6};
Double[] arr2 = {2.3,42.1,1.6,6.43};
ZipIterator<Integer,Double> zip = new ZipIterator<>(arr1,arr2);
Edited my answer based on the comment by #4castle

Overload method with generic method and parameter

I've been looking through countless pages trying to figure out if it is possible to overload a method with a List parameter, with another method that has the generic return parameter List<E>. I understand that this should always try to be avoided, but it's for an assignment, and I would like to keep both methods included if it is actually possible. Below is the code for the two methods:
// This method returns an array of the info.
Object[] toArray(List data) {
Object[] array = new Object[data.size()];
for (int i = 0; i < data.size(); i++) {
array[i] = data.get(i);
}
return array;
}
// Overloaded version that uses Generics.
E[] toArray(List<E> data) {
return toArray(data);
}
My question is, besides changing the name, is there any way to differentiate the parameters that would allow both methods to be in place, and that would not cause a name clash error? Any help is appreciated. Thank you in advance!
Note: someone asked the question "How to overload a method with generic parameter in java?" on StackOverflow, which was close to what I needed, but not quite.
You don't need to overload the method because Java does this implicitly for you with raw types.
<E> E[] toArray(List<E> data) {
If you call
List objs = ...
Object[] array = toArray(objs);
and if you use generics you get generics
List<String> strs =
String[] array = toArray(objs);
The real problem you have is there is no way to implement toArray. This is because the generic type is not known at at runtime. i.e. you can't create an array of E nor can you cast an Object[] to a String[] for example.
What you can do is pass the type of the array to use.
public static Object[] toArray(List list) {
return toArray(list, Object.class);
}
public static <E> E[] toArray(List<? extends E> list, Class<E> eClass) {
#SuppressWarnings("unchecked")
E[] array = (E[]) Array.newInstance(eClass, list.size());
for (int i = 0; i < array.length;i++)
array[i] = list.get(i);
return array;
}

Casting Object[] to String[] error

I need this code, but i get this error:
Ljava.lang.Object; cannot be cast to java.lang.String
public Object[] getAllKeys (){
return keys.toArray(new Object[keys.size()]);
}
public String[] getNames (){
return ((String[])super.getAllKeys()); <- Error here. Can't cast, why?
}
The type of the array is Object[] so it cannot know that it contains only Strings. It is quite possible to add a non-String object to that array. As a result the cast is not allowed.
You can return Object[] and then cast each of the objects within that array to string. i.e. (String)arr[0] or you can create a new String[] array and copy all the elements over before returning it.
toArray() returns an array of Objects. If you want to create an array of Strings out of it, you will have to do it yourself. For example,
Object [] objects = super.getAllKeys();
int size = objects.size();
String [] strings = new String[size];
for (int i = 0; i < size; i++)
strings[i] = objects[i].toString();
or something similar... Hope this is useful.
Every String is an Object. Every Object is NOT a String.
You cannot do the cast, because even though Object is a base class of String, their array classes Object[] and String[] classes are unrelated.
You can fix this problem by introducing an additional method that allows taking a typed array:
public Object[] getAllKeys (){
return getAllKeys(new Object[keys.size()]);
}
// Depending on your design, you may want to make this method protected
public <T> T[] getAllKeys(T[] array){
return keys.toArray(array);
}
...
public String[] getNames (){
return super.getAllKeys(new String[keys.size()]);
}
This code takes advantage of the other overload of toArray, which accepts a typed array as an argument.
This cannot be done implicitly since the runtime cannot know that the elements in Object[] are all String types.
If you don't want to code a loop yourself, then one way to coerce is to use
String[] myStringArray = Arrays.asList(keys).toArray(new String[keys.length]);
I think that this will happen without any string copies being taken: asList() binds to the existing array data and toArray uses generics which are removed at runtime anyway due to type erasure. So this will be faster than using toString() etc. Don't forget to deal with any exceptions though.
Try the following snippet
Object[] obj = {"Red","Green","Yellow"};
String[] strArray = (String[]) obj; // Casting from Object[] to String[]

Categories

Resources