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!
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 <string>
#include <set>
#include <iostream>
#include <functional>
using namespace std;
class Cliente {
public:
Cliente (int id_, string nome_) : id (id_), nome (nome_) { }
/// <summary>
/// Ordenação default por id
/// </summary>
bool operator < (const Cliente& other) const {
return id == other.id? (nome < other.nome) : (id < other.id);
};
friend ostream& operator << (ostream& os, const Cliente& cli);
friend class OrdenarClientesPorNome;
private:
int id;
string nome;
};
ostream& operator << (ostream& os, const Cliente& cli) {
os << "[" << cli.id << "=" << cli.nome << "]";
return os;
};
class OrdenarClientesPorNome {
public:
bool operator() (const Cliente& cli1, const Cliente& cli2) const {
return cli1.nome == cli2.nome ? (cli1.id < cli2.id) : (cli1.nome < cli2.nome);
};
};
int main (int argc, char *argv[]) {
set<Cliente> clientes;
clientes.insert (Cliente (45, "Vesgo"));
clientes.insert (Cliente (69, "Sabrina"));
clientes.insert (Cliente (51, "ET"));
cout << "Listando os clientes por id:" << endl;
for (set<Cliente>::const_iterator it = clientes.begin(); it != clientes.end(); ++it) {
cout << *it << endl;
}
cout << endl << "Listando os clientes por nome:" << endl;
set<Cliente, OrdenarClientesPorNome> clientesPorNome;
clientesPorNome.insert (Cliente (45, "Vesgo"));
clientesPorNome.insert (Cliente (69, "Sabrina"));
clientesPorNome.insert (Cliente (51, "ET"));
for (set<Cliente, OrdenarClientesPorNome>::const_iterator it2 = clientesPorNome.begin(); it2 != clientesPorNome.end(); ++it2) {
cout << *it2 << endl;
}
}