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.

17
  • How is elements declared? Commented Dec 21, 2014 at 15:59
  • @lea protected E[] elements = null; Commented Dec 21, 2014 at 16:01
  • 1
    Try to generify your Stack class by using class Stack<T extends E> { ... }. Then just use Stack<E>. So you don't need to do class casting. Commented Dec 21, 2014 at 16:04
  • 4
    Can you show us the line where this happens as neither of these lines need to have a class cast exception. Commented Dec 21, 2014 at 16:10
  • 4
    Just creating a new instance of your Stack (without the IStack interface) I can not reproduce your problem. No Exceptions. Commented Dec 21, 2014 at 16:18

5 Answers 5

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;
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for explanation. That is how I did it now.
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.
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.

Comments

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));

Comments

1

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

2 Comments

Random ran = new Random(); Stack<Integer> st = new Stack<Integer>(); st.push(ran.nextInt(100));
Yes. When you write Stack st = new Stack();, java understands it as Stack<Object> st = new Stack<Object>();, 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.
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;
}
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.