How to return generic methods? - java

I'm doing my homework right now and I stumbled on a, probably pretty easy, 'problem'.
I have to build a stack and I need to complete the following method:
public E top()
{
if(!isEmpty()) {
/*
* return top element
* which would be st[pos-1]
*/
}
return null;
}
I use
//to save elements
private Object[] st;
//for position in array
private int pos = 0;
I tried to return my array, but it tells me "Cannot convert from Object to E". Now I don't know how to continue.
//edit: my constructor
public Stack(int size)
{
st = new Object[size];
//st = new E[size]; doesn't work
}

You need to cast:
E element = (E) st[pos-1];
You will get an unchecked cast warning, though. This is safe to suppress, provided you ensure that only instances of E are added to the array.
void add(E element) {
st[pos++] = element;
}
(And that is pretty much what java.util.ArrayList does, btw)

Your Array should be of type E
private E[] st;

Related

Java Generics and type transform

I just start learning Generics of Java. It confused me when I try to read some sample examples, just as below. I know it's something related to the type erasure, but I can not figure out what's going wrong.
package graph;
import java.util.*;
public class JustTest<T> {
public static void main (String[] args) {
ArrayQueue<String> q = new ArrayQueue<String>();
q.offer(new String("string_1"));
q.offer(new String("string_2"));
Object[] ob = q.getArray();
System.out.println(ob);
// The lines below went wrong when running
// Error information:
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
String[] str = q.getArray();
System.out.println(str);
}
}
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array = (T[]) new Object[MINIMUM_SIZE];
private int lastIndex = 0;
public T[] getArray () {
return array;
}
public T getElement () {
return array[0];
}
public boolean offer(T value) {
array[lastIndex % array.length] = value;
lastIndex++;
return true;
}
}
The problem lies with the unchecked cast in your ArrayQueue class:
private T[] array = (T[]) new Object[MINIMUM_SIZE];
You basically say here that an Object[] is actually a T[], but this cast is 'unchecked' so there is never a check at runtime. If there was, this cast would fail, since an Object[] is not a T[] unless T is Object. But since the cast is unchecked, the program doesn't crash on this line.
The program crashes on the line where the compiler inserts an implicit cast, at the point where T is a known concrete type:
String[] str = q.getArray();
Here you go from T[] to String[], so the compiler will insert an implicit cast here:
String[] str = (String[]) q.getArray();
And this is where it blows up, since your earlier cast to T[] was faulty.
With the interface you currently have, the most convenient solution would be to pass a reference to a constructor of T[], so you can actually create a T[]:
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array;
...
public ArrayQueue(IntFunction<T[]> arrayCons) {
array = arrayCons.apply(MINIMUM_SIZE);
}
...
ArrayQueue<String> q = new ArrayQueue<>(String[]::new); // passing a constructor reference
Or use a similar solution to what API Collections do, using an Object[] instead of a T[] array, and require a reference to a T[] constructor when calling getArray:
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private Object[] array = new Object[MINIMUM_SIZE];
...
public T[] getArray(IntFunction<T[]> arrayCons) {
T[] ret = arrayCons.apply(size()); // implement size
System.arrayCopy(array, firstIndex, ret, 0, size());
return ret;
}
public T getElement() {
return (T) array[0]; // a safe unchecked cast, since you only put Ts into the array.
}
}
Update
I missed up something last time, if you are learning generics, just picked up a little twisted example, you probably need to start with some classes like Pair, but well.
I Made this changes to your ArrayQueue class:
The #SuppressWarnings is added to avoid the error casting, because we know that the cast we made here manually is right, so, suppress this warning is fine in this case.
I also added two constructors, one parameter and two parameters, the first parameter is the Class<T> c is the type you will say it will have in the moment you create it, and the second one is if you want another sizes for your array.
in this line:
array = (T[]) Array.newInstance(c, MINIMUM_SIZE);
is how I made possible to create the generic array, with the Array.newInstance method, you can tell the class and the size, and the type is given as parameter, that's the trick.
#SuppressWarnings("unchecked")
class ArrayQueue<T> {
private static final int MINIMUM_SIZE = 1024;
private T[] array;
private int lastIndex = 0;
public ArrayQueue(Class<T> c){
array = (T[]) Array.newInstance(c, MINIMUM_SIZE);
}
public ArrayQueue(Class<T> c, int size){
array = (T[]) Array.newInstance(c, size);
}
public T[] getArray () {
return array;
}
public T getElement () {
return array[0];
}
public boolean offer(T value) {
array[lastIndex % array.length] = value;
lastIndex++;
return true;
}
}
And in the main class, for testing it:
public static void main(String[] args)
{
ArrayQueue<String> q = new ArrayQueue<String>(String.class);
q.offer(new String("string_1"));
q.offer(new String("string_2"));
String[] ob = q.getArray();
System.out.println(ob[0]);
System.out.println(ob[1]);
// The lines below went wrong when running
// Error information:
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
String[] str = q.getArray();
System.out.println(str[0]);
}
During runtime, Ts are replaced by Object, unless there is a bounding type. This means that
private T[] array = (T[]) new Object[MINIMUM_SIZE];
actually becomes
private Object[] array = new Object[MINIMUM_SIZE];
So when you call getArray, you get an array of Object, which can't be assigned a reference of type String[]. Hence the ClassCastException.
Each element in the array can, by itself, be cast from Object to String (specifically because it's String), but the array as a whole can't be cast.
Which is why you get
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
The [L means it's an array.
To get them as strings, you can do this thing:
String[] str = Arrays.toList(q.getArray()).stream().map(Object::toString()).collect(Collectors.toList()).toArray();
Which should work.

Cascading Generic Type declarations in Java

I am having a hard time finding this anywhere if this is a common problem but I am dealing with what is essentially a cascading type problem.
public class Graph<E> {
private LinkedList<Node<E>> nodes;
public Graph() {
this.nodes = new LinkedList<>();
}
public E[] getNodes() {
ArrayList<E> list = new ArrayList<>();
for (Node<E> node : nodes)
list.add(node.getObject());
return list.toArray(new E[0]); // any way to make this line work?
}
// other important stuff
}
I want to do something like this, however I can't instantiate the generic array this way. Where the getNodes() returns the content of the Nodes, not the Nodes themselves, but I can't figure out how.
I was thinking that the Node generic being defined by the Graph generic would mean that the Node class always has the same type as the Graph class. Is that not the case?
The Node class looks like
public class Node<E> {
private LinkedList<Edge> edges;
private E obj;
public E getObject() {
return obj;
}
// other useful stuff
}
Thanks for any help!
EDIT: all that is needed now is to make the returned Array of the right type. Is there a way to get an Array from an ArrayList that has a generic type assignment?
You need some form of reification of E in your getThings method.
If you want to keep the signature of getThings as it is, you can add a construtor parameter to provide the actual class E. With that class you can create an array to pass to the toArray(E[]) method of List<E>
private final Class<E> type;
private final List<E> list;
public CustomClass(Class<E> type) {
this.type = type;
this.list = new ArrayList<>();
}
#SuppressWarnings("unchecked")
public E[] getThings() {
Object[] reference = (Object[]) Array.newInstance(type, list.size());
return (E[]) list.toArray(reference);
}
Someone else came up with an answer that did not work but gave me an idea that ended up working, but they also put it in the comments section of the question so I will reiterate here and answer my own question.
This code works to solve the problem. I more or less lifted the logic from the ArrayList source code for their toArray(E[] a) function (with some of the meat of it cut out of course).
#SuppressWarnings("unchecked")
public E[] getNodes(E[] a) {
int size = nodes.size();
// creates an empty array of the right size and type
E[] arr =(E[]) java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
// fills that array with the correct data
for (int i = 0; i < size; i++)
arr[i] = nodes.get(i).getObject();
return arr;
}
Look at the ArrayList source code in order to see some logic that goes a step farther and accomplishes the same task in a way that is also Thread safe.

Why can't I initialize an array of custom class

I am trying to implement a basic hashmap in Java and am stuck on why I cannot declare an array of my custom class KVPair. I am getting an error after numerous trials of fixing my declaration of the array in my constructor:
contains = new KVPair[capacity];
When I tried this, I got a compile error saying that I "cannot create a generic array of HashMap.KVPair."
I have also seen from another stackexchange answer that suggested casting an array of objects into something else like this:
contains = (KVPair[])new Object[capacity];
When I do this, I get a run-time error saying "java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [MyHashMap$KVPair;."
Below I have included one of the constructors of my hashmap class as well as my KVPair class. Any help on how I can solve this issue would be much appreciated.
public class MyHashMap<K, V> implements Iterable<K> {
private static final int DEFAULT_CAPACITY = 200;
private static final double DEFAULT_LOAD_FACTOR = 0.7;
private int capacity; // the number of buckets in the map
private int size; // the number of items that have been put into the map
private double loadFactor;
KVPair[] contains;
// Constructs an empty map.
public MyHashMap() {
capacity = DEFAULT_CAPACITY;
this.loadFactor = DEFAULT_LOAD_FACTOR;
contains = (KVPair[]) new Object[capacity];
}
...
public class KVPair {
private K key;
private V value;
private KVPair next;
private int hash;
private KVPair(Object k, Object v){
key = (K) k;
value = (V) v;
next = null;
hash = k.hashCode();
}
public KVPair(Object k, Object v, KVPair nextKV){
key = (K) k;
value = (V) v;
next = nextKV;
hash = k.hashCode();
}
}
Usually implementations of collections that require an underlying generic array solve this problem by using a non-generic array of type Object[]. As long as the array is hidden as an implementation detail and that the method exposed by the collection are all generic, then it is completely type-safe to do so. You can see that in the JDK code in particular (for example: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList).
Another solution for instantiating generic arrays is to use Array.newInstance. However, this requires to pass the class of the generic type, which you most likely don't want to impose on the user of your API. Just for the record, here is as this can be used to create a generic array factory method:
public class ArrayUtils {
#SuppressWarnings("unchecked")
public static <T> T[] ofDim(Class<T> clazz, int length, T defaultValue) {
T[] arr = (T[]) Array.newInstance(clazz, length);
for (int i = 0; i < length; i++) arr[i] = defaultValue;
return arr;
}
}
In short:
if your generic array can be hidden as an implementation detail, use a bare Object[]
if your generic array is going to be passed and potentially mutated by an external class, you need to ensure runtime type safety using Array.newInstance

How do I set a generic object in my array to 0/null?

I have an array that in my class declaration holds objects of generic type (later on, I will add <Integer> type objects). I want to delete the index at currentSize (which is an int). Eclipse complains by saying "Type mismatch: cannot convert int to E" regarding to the line
list[currentSize] = null;
Here's my code:
...
private int currentSize, maxCapacity;
...
public ArrayABC(){
currentSize = 0;
maxCapacity = 20;
list = (E[]) new Object[maxCapacity];
}
...
public E removeLast(){
if(currentSize == 0)
return null;
list[currentSize] = null;
currentSize--;
return list[currentSize];
}
Also note that after I try to assign the list[currentSize] as a return value. Do I have to cast it to <E> while doing so (very last line of the code)?
I guess a better way of putting this is, how do I destroy the object at location = currentSize?
Thank you for your help and support in advance!
It all depends on how your list is declared.
Also since you cast it to type parameter E you must use this type parameter somewhere in your class declaration like this: <E>
Below compiles for me just fine:
public class ArrayABC<E> {
private int currentSize, maxCapacity;
private E[] list;
public ArrayABC(){
currentSize = 0;
maxCapacity = 20;
list = (E[]) new Object[maxCapacity];
}
public E removeLast(){
if(currentSize == 0)
return null;
list[currentSize] = null;
currentSize--;
return list[currentSize];
}
}
The only snag is that Eclipse is highlighting:
list = (E[]) new Object[maxCapacity];
as warning with unsafe access, but there is no clean way to create instance of any object with type E due to type erasure.

Implement own ArrayList<> without using collections

I am trying to implement my own ArrayList without using java collections for practice purposes. At this stage I want to implement two of main methods, add(E) and get(int) tp get the idea. My code is given below. However I encountered few issues:
The line "return (E) myData[index]" issues warning "Type safety: Unchecked cast from Object to E". How can I address that
The Java 7 implementation of ArrayList.add(T), returns a boolean. Under what circumstances the add() has to return false. Under what logic it return false and when returns true?
Where can I find the source code of java 7 implementation of ArrayList
PS. Kindly don't just answer question 3 and refer me to the sucrose code for one and two!
import java.util.Arrays;
public class MyArrayList<E>{
private final int DEFAULT_SIZE=2;
private Object[] myData = new Object[DEFAULT_SIZE];
private int actSize=0;
public boolean add(E data){
if (actSize>=myData.length/2){
increaseSize();
}
myData[actSize++] = data;
return true;//when can it be false?
}
private void increaseSize()throws RuntimeException{
myData = Arrays.copyOf(myData, myData.length*2);
}
public E get(int index) throws RuntimeException{
if (index >= actSize){
throw new IndexOutOfBoundsException();
}
return (E) myData[index];
}
public static void main(String[] args) {
MyArrayList<String> arList = new MyArrayList<>();
arList.add("Hello");
arList.add("Bye bye!");
System.out.println(arList.get(1));// prints Bye bye! which is correct
}
}
The line "return (E) myData[index]" issues warning "Type safety:
Unchecked cast from Object to E". How can I address that
Suppress the warning
#SuppressWarnings("unchecked")
The Java 7 implementation of ArrayList.add(T) returns a boolean.
Under what circumstances the add() has to return false. Under what
logic it return false and when returns true?
See the javadoc
Returns:
true (as specified by Collection.add(E))
It always returns true.
Where can I find the source code of java 7 implementation of ArrayList
In your JDK installation's src.zip archive or find it online by simply searching
java ArrayList source code
The line "return (E) myData[index]" issues warning "Type safety: Unchecked cast from Object to E". How can I address that?
You're always going to have this unchecked cast warning, since you're working with a generic array. Generics and arrays don't really mix all that well, but the better convention is to have the generic type attached to the array anyway:
private E[] myData = (E[]) new Object[DEFAULT_SIZE];
You could always add #SuppressWarnings("unchecked") to the field itself to get that warning to go away.
#SuppressWarnings("unchecked")
private E[] myData = (E[]) new Object[DEFAULT_SIZE];
The Java 7 implementation of ArrayList.add(T), returns a boolean. Under what circumstances the add() has to return false. Under what logic it return false and when returns true?
This is a bit of an interesting question. Typically, one would expect that the restrictions on add come from Collections#add:
Collections that support this operation may place limitations on what elements may be added to this collection. In particular, some collections will refuse to add null elements, and others will impose restrictions on the type of elements that may be added. Collection classes should clearly specify in their documentation any restrictions on what elements may be added.
...but, since ArrayList is special in that it's designed to always expand its space when it's about to run out, it will (in theory) always be able to add something in. So, it should always return true.
Where can I find the source code of Java 7 implementation of ArrayList?
Grepcode is usually a good resource. You could also find it in src.zip if you downloaded the JDK with sources.
I do not think you could avoid that Type safety warning using a generic type. If it is really bothering you, you could add #SupressWarnings("unchecked").
Hmmm, I'm not sure about this one, but the Android implementation of Java, which isn't exactly the same says that it "always returns true". This is weird to me, but here's the link: http://developer.android.com/reference/java/util/ArrayList.html#add(E).
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/ArrayList.java, but definitely download the source and check it out.
I have done little explanation in comments because it is clear to understand.
public class MyArrayList<E extends Object> {
private static int initialCapacity = 5;
private static int currentSize;
private Object[] myArrayList = {}, temp = {};
private static int currentIndex = 0;
public static void main(String[] args) {
MyArrayList arrList = new MyArrayList();
arrList.add("123"); //add String
arrList.printAllElements();
arrList.add(new Integer(111)); //add Integer
arrList.printAllElements();
arrList.add(new Float("34.56")); //add Integer
arrList.printAllElements();
arrList.delete("123");
arrList.printAllElements();
arrList.delete(123);
arrList.printAllElements();
arrList.delete(123);
arrList.printAllElements();
}
public MyArrayList() { //creates default sized Array of Objects
myArrayList = new Object[initialCapacity]; //generic expression
/* everytime I cross my capacity,
I make double size of Object Array, copy all the elements from past myObject Array Object
*/
}
public MyArrayList(int size) { //creates custom sized Array of Objects
myArrayList = new Object[size];
}
public void add(Object anyObj) {
//add element directy
myArrayList[currentIndex] = anyObj;
currentSize = myArrayList.length;
currentIndex++;
if (currentIndex == currentSize) {
createDoubleSizedObjectArray(currentSize);
}
}
//print all elements
public void printAllElements() {
System.out.println("Displaying list : ");
for (int i = 0; i < currentIndex; i++) {
System.out.println(myArrayList[i].toString());
}
}
private void createDoubleSizedObjectArray(int currentSize) {
temp = myArrayList.clone();
myArrayList = new MyArrayList[2 * currentSize]; //myObject pointer big size data structure
// myObject = temp.clone(); //probably I can do this here as well. Need to check this
System.arraycopy(temp, 0, myArrayList, 0, currentSize);
}
void delete(Object object) {
//if already empty
if (currentIndex == 0) {
System.out.println("Already empty!");
return;
}
//you don't need to delete anything. I can simply override the storage
currentIndex--;
}
}

Categories