Skip to content
Advertisement

When building containers why is using Java Generics better than using the Object Class? (Java Generics & DataStructures)

So I have been reviewing my data structures and came across an interesting thought regarding Java generics and the Object class. I have implemented and run a “generic bag” in two different ways (Notice below: IObjectBag.java, ObjectBag.java, IGenericBag.java, and GenericBag.java) and have used them both (Notice: Below main.java and Output). I have removed some of the unnecessary code as per stack overflow rules but if you want the full implementation, let me know.

Also, I have researched the topic in many websites, books and courses in addition to looking at the source code for the ArrayList class here and I understand that my GenericBag is a better option than my ObjectBag but not well enough to explain it in a practical way during an interview. And I am confused that my GenericBag uses more casting operations than my ObjectBag in its implementation (see Remove and PrintBag).

  1. So, other than the syntactic sugar, why is my GenericBag better? Please use my classes as examples.

  2. Are there any important differences in runtime/overhead/space/time I am not noticing?

  3. How would you answer this question or expect it to be answered in an interview?

Bonus questions: If you want, please answer the bonus questions in the Main and GenericBag comments (I think I can answer them myself though, just want to hear your opinion).

IObjectBag interface:

public interface IObjectBag {
    void add(Object item);
    Object remove(Object item) throws NoSuchElementException;
    boolean isEmpty();
    int find(Object item);
    Object get(int index);
    int numItems();
}

ObjectBag class:

public class ObjectBag implements IObjectBag {
    private Object [] items; // the java class attribute that will hold out "ints"
    private int numItems;
    
    public static void printBag(IObjectBag bag) {
        for(int i = 0; i < bag.numItems(); i++) {
            System.out.println(bag.get(i));
        }
    }
    
    public ObjectBag(int size) {
        this.items = new Object[size]; // fills array with null values
        this.numItems = 0;
    }
    
    public void add(Object item){
        // adds item to end of bag
    }
    
    public Object remove(Object item) {
        int index = this.find(item);

        if(index == -1) throw new NoSuchElementException("oops nothing found");
        
        Object out = this.items[index];
        
        this.items[index] = null;
        this.numItems -= 1;
        
        if(index + 1 != this.items.length && this.items[index + 1] != null) {  
            for(int i = index; i < this.items.length; i++) {
                if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
            }
            
            this.items[this.items.length - 1] = null;
        }
        
        return out;
    }
    
    public int find(Object item) {
        // return index given item or -1 
    }
    
    public Object get(int index) {
        // returns item given index
    }
    
}

IGenericBag class:

public interface IGenericBag <T> {
    void add(T item);
    T remove(T item) throws NoSuchElementException;
    boolean isEmpty();
    int find(T item);
    T get(int index);
}

GenericBag class:

public class GenericBag<T> implements IGenericBag<T> {
    // private T[] items; can't use this b/c see comment in constructor
    private Object[] items;
    private int numItems;
    
    public static void printBag(GenericBag bag) {
        for(int i = 0; i < bag.numItems(); i++) {
            System.out.println(bag.get(i));
        }
    }
    
    public GenericBag(int size) {
        // this.items = new T[size]; Bonus: throws generic array creation error (why?)
        this.items = new Object[size];
        this.numItems = 0;
    }
    
    public void add(T item){
        this.items[this.numItems] = item;
        this.numItems += 1;
    }
    
    public T remove(T item) {
        int index = this.find(item);

        if(index == -1) throw new NoSuchElementException("oops nothing found");
        
        T out = (T) this.items[index];
        
        this.items[index] = null;
        this.numItems -= 1;
        
        if(index + 1 != this.items.length && this.items[index + 1] != null) {  
            for(int i = index; i < this.items.length; i++) {
                if(i + 1 != this.items.length) this.items[i] = this.items[i + 1];
            }
            
            this.items[this.items.length - 1] = null;
        }
        
        return out;
    }
    
    public int find(Object item) {
        // given object return index or throw exception
    }
    
    public T get(int index) {
        return (T) this.items[index];
    }
    
}

Main class:

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        System.out.println("Hello StackOverFlow!");
        Object int1 = new Integer(1);
        Object int2 = new Integer(2);
        Object int3 = new Integer(3);
        
        
        /* using my object bag ************************************************/
        System.out.println("using my object bag");
        IObjectBag myObjectBag = new ObjectBag(3);
                
        myObjectBag.add(int1);
        myObjectBag.add(int2);
        myObjectBag.add(int3);
        myObjectBag.remove(int2);
        
        ObjectBag.printBag(myObjectBag);
        
        /* using my generic bag ***********************************************/
        System.out.println("using generic bag");
        
        
        // Bonus Question: using object like above causes error at add method (why?)
        Integer int4 = new Integer(4); 
        Integer int5 = new Integer(5);
        Integer int6 = new Integer(6);
        
        GenericBag<Integer> myGenericBag = new GenericBag<Integer>(3); 
        //Bonus Question: using Interface decllaration like above causes error in print bag (why?) 
        
        myGenericBag.add(int4);
        myGenericBag.add(int5);
        myGenericBag.add(int6);
        myGenericBag.remove(int4);
        
        GenericBag.printBag(myGenericBag);
    }
    
}

Output:

Hello StackOverFlow!
using my object bag
1
3
using generic bag
5
6

Advertisement

Answer

Problems with your ObjectBag that are ‘automaticaly’ solved by the type safety offered by your GenericBag implementation:

  • Accessing an entry returns Object, at this stage you do not know of what type Object is.
  • You can insert any types of Objects (mixed) e.g a String and an Integer into the same list, this is an anti pattern and causes non readable code (try it with your Generics bag!)
  • Because your compiler knows the type of your GenericBag after you have declared it, at any stage of your code if you hover over your genericBag instance you will know its type, this makes your code more readable and also extendable for other people

Generics also offer way more, imagine you want your GenericBag to only accept numbers, then you could write it as follows:

public class GenericBag<T extends Number>

My suggestion for you is to read some articles on Java basics and especially Generics, having a praxis based way of learning is a good thing, but there are plenty articles that can give you some very nice theoretical insight on the matter.

https://www.baeldung.com/java-generics

Advertisement