Por que o compilador não detecta isso?

[code]import java.util.*;

class Dog { int size; Dog(int s) { size = s; } }

public class FirstGrade {

public static void main(String[] args) {

	TreeSet<Integer> i = new TreeSet<Integer>();
	TreeSet<Dog> d = new TreeSet<Dog>();

	d.add(new Dog(1));   d.add(new Dog(2));   d.add(new Dog(1));
	i.add(1);            i.add(2);            i.add(1);

	System.out.println(d.size() + " " + i.size());
}

}[/code]

What is the result?

A. 1 2
B. 2 2
C. 2 3
D. 3 2
E. 3 3
F. Compilation fails
G. An exception is thrown at runtime

A resposta correta é a G. Porém, eu gostaria de saber porque o compilador não consegue detectar que Dog não possui a interface Comparable implementado. Uma vez que eu declarei o TreeSet explicitamente como um treeSet que só aceita Dogs, pra mim o compilador teria condição de dar um erro em tempo de compilação. Caso fosse:

TreeSet d = new TreeSet(); d.add(new Dog(1)); d.add(new Dog(2)); d.add(new Dog(1));

ou mesmo

TreeSet d = new TreeSet<Dog>(); d.add(new Dog(1)); d.add(new Dog(2)); d.add(new Dog(1));

Eu concordo que não tem como o comipilador avisar nada. Mas, no caso, temos:

TreeSet<Dog> d = new TreeSet<Dog>(); d.add(new Dog(1)); d.add(new Dog(2)); d.add(new Dog(1));

o que para mim é informação suficiente para o compilador detectar que Dog não implementa a interface Comparable (o que vai dar problema para um treeset…)

Alguém possui alguma explicação do porquê o compilador não detecta isso?
Obrigado!

repare na declaração do método add da classe TreeSet:


public boolean add(E e)

reparou que nada impeça que vc adicione um objeto que não implemente a interface Comparable…
se a declaração fosse assim:


public boolean add(E extends Comparable<E>)

se a declaração fosse assim ai isso impediria de vc adicionar um objeto que não implemente a interface Comparable…

Devem haver motivos históricos também. A classe TreeSet está na linguagem desde a versão 1.4, antes de generics. Assim, se essa restrição fosse colocada provavelmente muita coisa antiga não iria mais funcionar.

Não é necessário que os objetos de um TreeSet sejam comparáveis. Um dos construtores do TreeSet recebe como parâmetro um Comparator e, nesse caso, os objetos não precisam estender Comparable.

De fato, é bem mais fácil fazer isso em Java que em C++. Embora a filosofia seja exatamente a mesma (se você quiser uma ordenação diferente da padrão, tem de passar um objeto comparador), isso em C++ é bem desajeitado. Vou mostrar um código que cria dois std::set (o equivalente do java.util.TreeSet). Um deles ordenando por id, outro por nome. Vocês vão ver que é meio desajeitado (tanto é que no C++0X o jeito de fazer isso é mais parecido com o do Java.)

#include &lt;string&gt;
#include &lt;set&gt;
#include &lt;iostream&gt;
#include &lt;functional&gt;
using namespace std;

class Cliente {
public:
    Cliente (int id_, string nome_) : id (id_), nome (nome_) { }
    /// &lt;summary&gt;
    /// Ordenação default por id
    /// &lt;/summary&gt;
    bool operator &lt; (const Cliente& other) const {
        return id == other.id? (nome &lt; other.nome) : (id &lt; other.id);
    };
    friend ostream& operator &lt;&lt; (ostream& os, const Cliente& cli);
    friend class OrdenarClientesPorNome;
private:
    int id;
    string nome;
};

ostream& operator &lt;&lt; (ostream& os, const Cliente& cli) {
    os &lt;&lt; &quot;[&quot; &lt;&lt; cli.id &lt;&lt; &quot;=&quot; &lt;&lt; cli.nome &lt;&lt; &quot;]&quot;;
    return os;
};


class OrdenarClientesPorNome {
public:
    bool operator() (const Cliente& cli1, const Cliente& cli2) const {
        return cli1.nome == cli2.nome ? (cli1.id &lt; cli2.id) : (cli1.nome &lt; cli2.nome);
    };
};

int main (int argc, char *argv[]) {
    set&lt;Cliente&gt; clientes;
    clientes.insert (Cliente (45, &quot;Vesgo&quot;));
    clientes.insert (Cliente (69, &quot;Sabrina&quot;));
    clientes.insert (Cliente (51, &quot;ET&quot;));
    cout &lt;&lt; &quot;Listando os clientes por id:&quot; &lt;&lt; endl;
    for (set&lt;Cliente&gt;::const_iterator it = clientes.begin(); it != clientes.end(); ++it) {
        cout &lt;&lt; *it &lt;&lt; endl;
    }
    cout &lt;&lt; endl &lt;&lt; &quot;Listando os clientes por nome:&quot; &lt;&lt; endl;
    set&lt;Cliente, OrdenarClientesPorNome&gt; clientesPorNome;
    clientesPorNome.insert (Cliente (45, "Vesgo"));
    clientesPorNome.insert (Cliente (69, "Sabrina"));
    clientesPorNome.insert (Cliente (51, "ET"));
    for (set&lt;Cliente, OrdenarClientesPorNome&gt;::const_iterator it2 = clientesPorNome.begin(); it2 != clientesPorNome.end(); ++it2) {
        cout &lt;&lt; *it2 &lt;&lt; endl;
    }
}

Você pode reduzir muito dessa verbosidade usando boost lambda e o boost for each.