Escolar Documentos
Profissional Documentos
Cultura Documentos
Para criar interfaces usamos uma sintaxe parecida com a das classes, substituindo a palavra class
por interface, por exemplo:
Ao contrário das classes, não existe uma interface "raiz" de todas as interfaces, como ocorre com a
classe Object.
A interface InterfaceExemplo, vista acima, não herda de nenhuma outra.
Note que a mesma convenção para nome de classe se aplica aos nomes das interfaces: iniciar com
letra maiúscula, seguida de letras minúsculas.
A constante PALAVRA do exemplo acima segue a convenção de nome de constantes em Java, que
é ter todas as letras maiúsculas.
O arquivo fonte de uma interface, da mesma forma que no caso de classes, também tem o nome
da interface com a terminação .java.
E o compilador gera da mesma forma um arquivo .class de mesmo nome.
Usando interfaces
Uma Interface funciona de forma análoga a uma classe abstrata.
Ou seja, ela não pode ser instanciada, mas pode ser como que "herdada" por uma classe.
A forma sintática para uma classe herdar de uma interface utiliza a palavra chave implements,
como no exemplo fictício abaixo:
O sentido de uma classe "herdar" de uma interface é similar ao de herdar métodos abstratos de
uma superclasse abstrata.
A classe fica obrigada a implementar concretamente todos os métodos declarados na interface, e
nas suas super-interfaces, ou a classe não compilará.
Note que a classe pode usar diretamente a constante definida na interface.
Nesse caso, após a palavra chave implements, escrevemos a lista das interfaces que a classe
implementa, separadas por vírgulas.
Note que a classe fica obrigada a implementar concretamente todos os métodos declarados em
todas as interfaces que implementa (isso incluindo todos os métodos declarados em todas as
superinterfaces de todas elas).
Por exemplo, supondo as interfaces dos exemplos acima, poderíamos declarar, em qualquer
método, variáveis como:
InterfaceExemplo ex;
InterfaceY y1;
Uma variável do tipo de uma interface pode referenciar qualquer objeto de qualquer classe que
implemente essa interface, ou qualquer subinterface dela.
Mas há uma restrição importante: a partir de uma referência de um tipo, só é possível acessar os
métodos definidos no nível desse tipo ou acima.
System.out.println(y1.f1()); // OK, método int f1() foi definido para o tipo InterfaceY
}
}
a) se o objeto t1 deve ser considerado menor (deve ficar antes) que t2, o método deve retornar
um inteiro negativo;
b) se o objeto t1 deve ser considerado maior (deve ficar depois) que t2, o método deve retornar
um inteiro positivo;
c) se o objeto t1 deve ser considerado igual a (deve ficar na mesma posição que) t2, o método
deve retornar zero.
O exemplo abaixo ajuda a compreender o uso dessa interface para ordenar um vetor de objetos
de uma classe qualquer.
Seja uma classe Data, para representar datas do calendário, com campos dia, mes e ano.
Se queremos que duas datas sejam "comparáveis", no sentido acima, a classe deve implementar a
interface Comparable<Data>, e implementatar um método public int compareTo (Data
d){....} que tenha a funcionalidade convencionada nesta interface.
O projeto Comparaveis.zip mostra essa classe e um exemplo de utilização para ordenar um vetor
de datas.
que necessariamente deverá existir na classe E. Nesse caso, para que isso possa ser
garantido, a classe E deve implementar a interface java.util.Comparable<E>, que
obriga a ter esse método.
Como deve funcionar o método compareTo:
Sejam x1 e x2 instâncias de E. Então, x1.compareTo(x2) deve retornar um inteiro negativo
se o objeto referenciado por x1 deva ficar antes de x2 na ordenação desejada, retornar um
inteiro positivo, caso x1 deva ficar depois de x2, e retornar o valor inteiro zero, caso x1 e x2
tenham a mesma posição relativa na ordenação desejada.
Esse método deve ter funcionalidade similar à do método compareTo() visto acima:
Sejam x1 e x2 duas referências do tipo T. Então, compare(x1, x2) deve retornar um
inteiro negativo se o objeto referenciado por x1 deva ficar antes de x2 na ordenação
desejada, retornar um inteiro positivo, caso x1 deva ficar depois de x2, e retornar o valor
inteiro zero, caso x1 e x2 tenham a mesma posição relativa na ordenação desejada.
Dizemos que o objeto passado como parâmetro para o construtor do TreeSet é "um
comparator", ou seja, é uma instância de uma classe que implementa a interface
Comparator<T>. Na prática, essa classe é feita para conter apenas o método compare(), e
o método compare() implementa o critério de ordenação desejado.
Observação muito importante para uso de TreeSets - compatibilidade de equals() com
compare() ou compareTo():
Como sabemos, um conjunto não pode conter referências "duplicadas". Mas o que significa
exatamente isso?
No caso de TreeSets, quando se vai adicionar um novo elemento ao conjunto, o método
comparador é acionado para verificar a posição deste elemento em relação aos que já estão no
conjunto. Caso o resultado seja zero, indicando que o novo elemento deve ficar na "mesma"
posição que algum outro que já esteja no conjunto, esse novo elemento NÃO será adicionado ao
conjunto, sendo considerado uma repetição. Isso ocorrerá MESMO que o teste de equals() retorne
o valor false.
Por exemplo, suponha um conjunto de Aluno, e que nessa classe o método boolean
equals(Object obj) considere que dois alunos são "iguais" caso possuam o mesmo valor de
DRE. Se o critério de ordenação usado pelo comparador for a ordem alfabética dos nomes, e se o
método compare(), ou compareTo(), retornar zero para dois alunos homônimos (mesmo que
com DRE's diferentes), então somente um desses alunos poderá entrar no conjunto.
Nesse caso dizemos que o método equals() e o método comparador utilizado não são
"compatíveis". Para que sejam compatíveis, o método comparador somente deve retornar zero
para os mesmos casos em que o método equals() retornar o valor true.
Como conseguir isso?
É relativamente fácil: basta fazer com que, nos casos em que o critério de ordenação do
comparador indicar a mesma posição relativa, o valor a ser retornado pelo método
compare/compareTo seja dado pelo critério definido no método equals() da classe dos
elementos do conjunto.
Voltando ao exemplo anterior, caso o critério compare dois alunos com exatamente o mesmo
nome, o método não deve retornar zero, mas sim o resultado da comparação dos valores de DRE
de cada um. Assim, somente retornaria zero caso os dois DRE's fossem idênticos, indicando
realmente ser o "mesmo" aluno que estaria se tentando incluir no conjunto.
Exemplo completo de uma classe comparadora de Aluno, usando o critério de comparar os nomes
em ordem alfabética ascendente, mostrando também o método equals() usado na classe Aluno:
class Aluno {
......
// método equals retorna true quando DRE's são iguais:
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Aluno)) return false;
Aluno aluno = (Aluno)obj;
return this.getDre().compareTo(aluno.getDre());
}
}
class ComparadorNome implements java.util.Comparator<Aluno>
public int compare(Aluno a1, Aluno a2) {
int res = a1.getNome().compareTo(a2.getNome());
if (res == 0) return a1.getDre().compareTo(a2.getDre());
return res;
}
}
Note que o método compare() acima jamais retornará zero com dois alunos de mesmo DRE,
mesmo que tenham exatamente o mesmo nome.
Outra observação importante: para poder ordenar um TreeSet<E> deve-se usar um
Comparator<T>, onde T deve ser do tipo E ou de algum supertipo de E (e não um subtipo):
Veja esse exemplo: ordenar um TreeSet<Pessoa>. Vamos supor que o TreeSet contenha
referência de Aluno e de Professor, subclasses de Pessoa. Evidentemente, o critério de
comparação usado não poderá usar nenhuma característica exclusiva de Aluno ou de Professor,
mas somente características comuns, que se encontram na classe Pessoa ou acima dela.
Poderemos usar um Comparator<Pessoa>, mas não um Comparator<AlunoGrad>.
Para indicar isso, o tipo do comparator a ser usado para construir um TreeSet<Pessoa> é escrito
da forma: Comparator<? super Pessoa>.
Ou seja, podemos comparar os elementos desse conjunto por nome, idade, etc, mas não por
nota. Um problema que surge aqui é que teremos que o método comparador terá que ter
compatibilidade com o método equals() existente na classe Pessoa ou acima dela.
Última observação: cuidado com a definição correta do método equals():
Para efeito de inclusão em sets, o método equals usado é o que tem a assinatura:
boolean equals(Object obj);
Esse é o método que precisa ser redefinido na classe dos elementos que queremos inserir em um
TreeSet. No caso do exemplo acima, note que NÃO definimos o método:
boolean equals(Aluno a);
O operador instanceof:
No método equals() usado no exemplo acima usamos o operador instanceof.
O operador instanceof compara um objeto com um tipo específico. É usado para testar se um
objeto é uma instância de uma classe, uma instância de uma subclasse, ou uma instância de uma
classe que implementa uma determinada interface.
Por exemplo, se Aluno é subclasse de Pessoa, e se Pessoa implementa Comparable, e se a
referência alu aponta para um objeto da classe Aluno, então:
alu instanceof Pessoa – dá true
alu instanceof Aluno – dá true
alu instanceof Comparable – dá true