Data Structures - Creating a Bag in Java

Christian M. Garcia picture Christian M. Garcia · Feb 10, 2015 · Viewed 11.1k times · Source

I am working on creating a bag of cards for a blackjack game in my CS course. This particular project requires that I create a bag to hold my 52 cards. Keep in mind that I am trying to assure that there are 4 types of each card, Queens, Kings, Jacks, and Aces all included. I keep getting an error in my main: Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer; at Main.main(Main.java:14)

If anyone can please help me to get this bag running properly, it would be greatly appreciated.

Here is my code:

public class Bag<T> 
{
    T[] cards;
    private final int DEFAULT_CAPACITY = 52;
    private int numberOfEntries;

    public Bag()
    {
        this.cards = (T[]) new Object[DEFAULT_CAPACITY];
        numberOfEntries = DEFAULT_CAPACITY;
    }

    public int getCurrentSize()
    {
        return numberOfEntries;
    }

    public boolean isFull()
    {
        return numberOfEntries == DEFAULT_CAPACITY;
    }

    public boolean isEmpty()
    {
        return numberOfEntries == 0;
    }

    public boolean add(T newItem)
    {
        boolean result = true;
        if(isFull())
        {
            result = false;
        }

        else
        {
            cards[numberOfEntries] = newItem;
            numberOfEntries++;
        }

        return result;
    }

    public boolean remove()
    {
        boolean result = true;
        if(numberOfEntries > 0)
        {
            numberOfEntries--;
        }
        else
            result = false;

        return result;
    }

    public void clear()
    {
        numberOfEntries = 0;
    }

    public int getNumOf(T anItem)
    {
        int count = 0;

        for(int i = 0; i < cards.length; i++)
        {
            if(anItem.equals(cards[i]))
            {
                count++;
            }
        }

        return count;
    }

    public boolean contains(T anItem)
    {
        boolean found = false;

        for (int i = 0; !found && (i < numberOfEntries); i++)
        {
            if(anItem.equals(cards[i]))
            {
                found = true;
            }
        }

        return found;
    }

    public T Grab()
    {
        int random = (int)(Math.random() * DEFAULT_CAPACITY);
        if(!isEmpty())
        {
            cards[random] = null;
            numberOfEntries--;
            return cards[random];
        }

        else
            return null;
    }

    public int getFrequencyOf(T anItem)
    {
        int counter = 0;

        for(int i = 0; i < numberOfEntries; i++)
        {
            if(anItem.equals(cards[i]))
            {
                counter++;
            }
        }

        return counter;
    }

}

public class Main {

    public static void main(String[] args)
    {
        //Accesses the Bag class
        Bag<Integer> bag = new Bag<Integer>();

        //Sets up 52 cards (13*4). 4 of each type
        for (int i = 1; i <= 13; i++) 
        {

            for (int j = 1; j <= 4; j++) {
                bag.cards[i*j] = i;
                //if the card is an ace and not equal to 1
                if(i == 1)
                    bag.cards[i*j] = 11;
                //handles the king, queen, and jack cards
                else if (i==11||i==12||i==13)
                    bag.cards[i*j] = 10;    
            }

            bag.add(1);
        }
    }
}

Answer

Bart Kiers picture Bart Kiers · Feb 10, 2015

Don't provide access to your T[] cards variable. Make it private and create a set method in your Bag like this:

public void set(int index, T item) {
  // assume !full AND 0 <= index < cards.length 
  this.cards[index] = item;
}

Then, instead of doing:

bag.cards[i*j] = 10;

you then do:

bag.set(i*j, 10);    

The fact that you get a class-cast exception is because type erasure: your T[] only lives at compile time. After compilation, it will just have become a Object[]. That is why your direct access cards[0] = 123 throws this exception (Integer 123 cannot be put inside a Object[]).

The set(int index, T value) I suggested works, because after compilation, that will just become set(int index, Object value), and hence: no class-cast exception.

EDIT

You can test the following quick demo:

class Bag<T> {

  private T[] cards;

  public Bag() {
    this.cards = (T[]) new Object[10];
  }

  public void set(int index, T value) {
    this.cards[index] = value;
  }

  @Override
  public String toString() {
    return "Bag{cards=" + java.util.Arrays.toString(cards) + "}";
  }

  public static void main(String[] args) {
    Bag<Integer> bag = new Bag<Integer>();
    bag.set(0, 10);
    bag.set(1, 20);
    bag.set(2, 30);
    System.out.println(bag);
  }
}

on Ideone, which will print:

Bag{cards=[10, 20, 30, null, null, null, null, null, null, null]} 

You can also simply remove the generics from your cards variable like this:

class Bag<T> {

  private Object[] cards;

  public Bag() {
    this.cards = new Object[10];
  }

  public void set(int index, T value) {
    this.cards[index] = value;
  }
}

For inspiration, you can always have a look at the source of core Java classes that resemble your own. In this case, that would be the java.util.ArrayList: http://www.docjar.com/html/api/java/util/ArrayList.java.html