3

I have written a generic class and below is the constructor of the class. I want to do something like this as written in line

elements = (E[])new Object[size] 

Because I do not know the generic type on the run time therefore it will throw an exception.

public class Stack<E> implements IStack<E> {
protected E[] elements = null;
protected int top = -1;
protected int size= 0;
private static final int DEFAULT_CAPACITY = 10;

public Stack(){
    this(DEFAULT_CAPACITY);
}

public Stack(int size){
    if(size <0){
        throw new IllegalArgumentException("Initial capacity cannot be negative or zero");
    }
    ArrayList<Integer> ar = new ArrayList<Integer>();
    elements = (E[])new Object[size];
}
}

Is there any way to solve such problems? The declaration of E is

protected E[] elements = null;    

This is how I am trying to call

Random ran = new Random();
Stack<Integer> st = new Stack<Integer>();
st.push(ran.nextInt(100));

Update Guys, Thanks for the help. I was messing around with generics so the problem was being created. Here is all the code which created the problem -

public class StackMain {
    public static void main(String[] args) {
        MinMaxStack minMaxStack = new MinMaxStack();
        Random ran = new Random();
        for (int k = 0; k < 10; k++) {
            minMaxStack.push(ran.nextInt(100));
        }
        System.out.println(minMaxStack);
    }
    }

public class MinMaxStack extends Stack<Integer> implements IMinMaxStack<Integer>{

private int min;
private int max;
/*
 * Approach 1:
 * For push method we can push and update the minimum/maximum value 
 * For pop method we will be traversing whole stack to find out the new minimum/maximum
 *
 */
@Override
public void push(Integer element){
    if(isEmpty()){
        this.min = element;
        this.max = element;
        elements[top+1] = element;
        size++;
    }else{
        if(element < min){
            min = element;
        }
        if(element > max){
            max = element;
        }
        elements[top+1] = element;
        size++;
    }
}
}

public  class Stack<E> implements IStack<E> {
protected E[] elements = null;
protected int top = -1;
protected int size= 0;
private static final int DEFAULT_CAPACITY = 10;

public Stack(){
    this(DEFAULT_CAPACITY);
}

public Stack(int size){
    if(size <0){
        throw new IllegalArgumentException("Initial capacity cannot be negative or zero");
    }
    elements = (E[])new Object[size];
}

public void push(E element) {
    ensureCapacity();
    elements[top+1] = element;
    size++;
}
}

public interface IStack<E> {    
public void push(E element );
}


public interface IMinMaxStack<E> extends IStack<E> {    
public int min();   
public int max();   
}

Update 2: Seems, other than passing the class type as mentioned in an answer below, there is no way we can do this.

Avneesh
  • 145
  • 2
  • 2
  • 11

5 Answers5

6

Here is the most-minimal code necessary to reproduce your exception.

class Stack<E> {
    protected E[] elements = (E[])new Object[1];
}

class IntStack extends Stack<Integer> {
    void push(Integer i) {
        // subtly accessing elements as Integer[] which it's not
        elements[0] = i;
    }
}

Java generics are implemented with type erasure so after compilation, this code translates to something like this:

class Stack {
    protected Object[] elements = new Object[1];
}

class IntStack extends Stack {
    void push(Integer i) {
        // throws ClassCastException
        ((Integer[])elements)[0] = i;
    }
}

Clearly a new Object[] is not an Integer[]. Notice how the cast gets moved to somewhere you did not explicitly put it. This is why (E[])new Object[size] was an unchecked cast and displayed a warning.

Instead, you should use Object[] and perform the unchecked cast only when you need to return an element to the outside world.

class Stack<E> {
    private Object[] elements;
    private int size;

    Stack(int len) {
        elements = new Object[len];
    }

    void push(E e) {
        elements[size] = e;
        size++;
    }

    E pop() {
       @SuppressWarnings("unchecked");
       E e = (E)elements[size - 1];
       size--;
       return e;
    }
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Thanks for explanation. That is how I did it now. – Avneesh Dec 21 '14 at 21:03
  • He doesn't need to declare it as `Object[]` -- his code as is (with `elements` declared as `E[]`) will work fine if he made `elements` `private`. – newacct Dec 23 '14 at 00:54
5

Basically, when you do (E[])new Object[size], it is a lie. The object's actual runtime class is Object[], which is not a subtype of E[] for whatever E is (unless E is Object). So the cast is, theoretically, incorrect. However, this does not create any immediate problems because inside the Stack class, E is erased to its upper bound, in this case Object. So inside the Stack class, we can use elements as E[], and put E in and get E out of it, with no problem.

A problem only occurs when the (incorrect) fact that elements is type E[] is "exposed" to the outside of the class, outside of the scope of the erasure of E, into a scope where someone has a concrete type argument for E. This usually happens when someone inadvertently makes elements public, or implements a method that returns it to the outside like

E[] getElements() {
    return elements;
}

Then on the outside of the class, someone has a Stack<SomeSpecificType>, and call this method, and expect a SomeSpecificType[], which is not what it gets.

However, your Stack class does not have such a method. So how are you "exposing" elements? The answer is that elements is protected, and is therefore "exposed" to subclasses. In this case, the subclass, MinMaxStack, extends Stack with a specific type for E, therefore, it "sees" elements as a specific type of array, which it is not.

newacct
  • 119,665
  • 29
  • 163
  • 224
4

I believe the usual way is to pass the Class into the constructor, and use Array.newInstance(Class<?>, int...) like

public Stack(Class<E> cls, int size){
    if(size <0){
        throw new IllegalArgumentException("Initial capacity cannot be "
            + "negative or zero");
    }
    elements = (E[]) Array.newInstance(cls, size);
}

Edit

From your update, please don't use raw-types. With Java 7 and above you can use the diamond operator <> like

Stack<Integer> st = new Stack<>(); 
st.push(ran.nextInt(100));

With earlier versions you specify the generic type like

Stack<Integer> st = new Stack<Integer>(); 
st.push(ran.nextInt(100));
Community
  • 1
  • 1
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
1

It's clear now. You're trying to create your Stack without generic type. Consider Stack<Integer> st = new Stack<>(); instead.

Everv0id
  • 1,862
  • 3
  • 25
  • 47
  • Random ran = new Random(); Stack st = new Stack(); st.push(ran.nextInt(100)); – Avneesh Dec 21 '14 at 16:30
  • 1
    Yes. When you write `Stack st = new Stack();`, java understands it as `Stack st = new Stack();`, it causes your `Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer` error. So u need to specify your actual type to pass `Integer` instead of `Object`. – Everv0id Dec 21 '14 at 16:33
1

Here is how you would fix it, you should not ever do (T[]) new Object[DEFAULT_CAPACITY]; instead an abstraction should be there for example (T[]) new Comparable[DEFAULT_CAPACITY];

public class ArrayStack<T extends Comparable<? super T>> implements Stack<T> {

private final int DEFAULT_CAPACITY = 50;

private int top;
private T[] elements;

@SuppressWarnings("unchecked")
public ArrayStack() {
    this.elements   = (T[]) new Comparable[DEFAULT_CAPACITY];
    this.top        = 0;
}
}
craftsmannadeem
  • 2,665
  • 26
  • 22