class MyGenericList<T> {

    T data;
    MyGenericList<T> next;

    MyGenericList() {
	data = null;
	next = null;
    }

}


class MyReallyGenericUnboudedStack<T> {

    MyGenericList<T> stack;
 
    int numElems;

    MyReallyGenericUnboudedStack() {

	stack = new MyGenericList<T>();

    }

    void push(T o) {

	MyGenericList<T> x = new MyGenericList<T>();
	x.data = o;
	x.next = stack.next;
	stack.next = x;
	numElems++;

    }

    T pop() {

	if ( stack.next != null ) {

	    T retval = stack.next.data;
	    stack.next = stack.next.next;
	    numElems--;
	    return retval;

	}
	else {
	    System.out.println("Can't pop from empty stack, stupid!");
	    return null;
	}
    }


}




class MyReallyGenericStack<T> {

    T[] boundedStack;
    int numElems;

    MyReallyGenericStack(int maxElems) {

        // Problem: Cannot instantiate T-objects with
        // default constructor, since we do not know
        // whether this constructor exists for T:
        //
        // boundedStack = new T[maxElems];
        // 
        // results in a compile error.

 
        // Work-around: instantiate array of
        // Object, and do type cast:
	boundedStack = (T[])new Object[maxElems];

	numElems = 0;

    }

    void push(T o) {

	if ( numElems < boundedStack.length ) {
	    boundedStack[numElems++] = o;
	}
	else {
	    System.out.println("I'm full, buster!");
	}

    }

    T pop() {

	if ( numElems > 0 ) {
	    return boundedStack[--numElems];
	}
	else {
	    System.out.println("Can't pop from empty stack, stupid!");
	    return null;
	}

    }


}


class MyNotSoGenericStack {

    Object boundedStack[];
    int numElems;

    Class admissibleType;

    MyNotSoGenericStack(int maxElems, Class c) {

	numElems = 0;
	boundedStack = new Object[maxElems];
	admissibleType = c;

    }

    void push(Object o) {

        // Alternativ: if ( o instanceof String)

	if ( o.getClass() == admissibleType ) {

	    if ( numElems < boundedStack.length ) {
		boundedStack[numElems++] = o;
	    }
	    else {
		System.out.println("I'm full, buster!");
	    }

	}
	else {
	    System.out.println("This object is of evil type: " 
			       + o.toString());
	}

    }

    Object pop() {

	if ( numElems > 0 ) {
	    return boundedStack[--numElems];
	}
	else {
	    System.out.println("Can't pop from empty stack, stupid!");
	    return null;
	}

    }


}



public class MyGenerics {

    public static void main(String[] args) {

	MyNotSoGenericStack s0 
            = new MyNotSoGenericStack(10,String.class);

	s0.push("abc");
	s0.push("cde");
	s0.push(new Integer(19));

	String x = (String)s0.pop();
	System.out.println("pop'ed string is " + x);


	MyReallyGenericStack<String> s1 
	    = new MyReallyGenericStack<String>(10);


	s1.push("asdadd");
	s1.push("mmmmmmmmmmmmmmmm");
	// results in a compile error:
        //s1.push(new Integer(19));

	x = (String)s1.pop();
	System.out.println("pop'ed string is " + x);


	MyReallyGenericUnboudedStack<String> s2 
                    = new MyReallyGenericUnboudedStack();

	s2.push("item0");
	s2.push("item1");
	s2.push("item2");
	s2.push("item3");
	// results in a compile error:
        // s2.push(new Integer(19));

	while ( (x = (String)s2.pop()) != null )
	    System.out.println("pop'ed string is " + x);

    }


}

