Por que uma List <type> absorve elementos não-tipo em tempo de compilação e execução?

Eu tenho essa demo, que eu não preciso de uma solução particular com uma arquitetura redesenhada, mas apenas entender por que se comporta assim e qualquer coisa que eu estou perdendo para evitá-lo. Eu estou querendo saber porque:

O compilador permite a inserção de um elemento que não é o tipo da lista na lista

A exceção ClassCast é lançada quando tentamos obter o elemento em vez de empurrá-lo

import Test.*; //Inner classes
import java.util.List;
import java.util.ArrayList;

public class Test<E extends Object> {

    private List<E> list = new ArrayList<E>();
    public Test() {}

    public static void main(String[] args) {
        Test<String> a = new Test<String>();
        a.addElement(new String());
        a.addElement(new Integer(0)); // No complain in comp/run-time, I dont understand why CastException is not thrown here
        String class1 = a.getElement(0); 
        String class2 = a.getElement(1); //No complain in comp but ClassCastException: Integer cannot be cast to String
        //Integer class3 = a.getElement(1); //Compilation error
    }

    public void addElement (Object node) { list.add((E) node); } //No complain in comp/run-time
    public E getElement(int index)       { return list.get(index); }
}

O que poderia ser uma solução? Observe a linha addElement onde eu preciso passar um SuperClass, em vez de digitar E. Isso é necessário para minha arquitetura, isso é apenas uma simples demonstração de simulação. Mas, de qualquer forma, o casting para o tipo E deve agir como desejado e lançar um CastExeption em tempo de execução, não deveria?

questionAnswers(2)

yourAnswerToTheQuestion