Neste módulo veremos:
Uma interface é uma maneira de declarar o comportamento de uma classe.
Nesta declaração não especificamos
exatamente como acontece internamente cada comportamento.
Para uma interface
são declarados somente o nome
do método e seus parâmetros. Esta especificação de parâmetros
é feita através de seus tipos.
Vejamos um exemplo de uma interface:
public interface mp3Player { public void play(); public void stop(); }
Abaixo temos uma segunda declaração de interface para o TAD Pilha.
/** * Interface para o TAD Pilha * @author EESR * @version fev 2006 */ public interface PilhaTAD { //metodos de acesso public boolean vazia(); //metodos para atualizacao public void push(Object c); public Object pop(); }
É redundante declarar uma interface public
como
também os seus métodos, pois uma interface é um meio
de comunicação entre os objetos que irão implemetá-la.
De qualquer modo, está é uma primeira versão de nossa interface,
podemos sempre melhorá-la.
E agora a declaração de uma classe usando esta interface:
public class Pilha implements PilhaTAD { ...
Para utilizar uma interface deve-se especificar a classe que
implementa a interface. Isto é feito através da palavra
chave implements
. A classe que obedece a interface
deve implementar todo e cada método da interface, deve usar
o mesmo número de argumentos e o mesmo tipo de retorno especificado
na interface.
Se a classe que implementa a interface deixar qualquer método definido na interface fora de sua definição a classe que foi declarada como interface é agora uma classe abstrata e deve ser declarada como.
Se a classe não implementa algum método declarado na interface
então ela deve se basear numa classe abstract
.
Lembre-se que implementar uma interface é como firmar um contrato
com o compilador. Deve-se especificar (definir e implementar) todos
os métodos da interface para a classe que a implementa.
Usa-se uma superclasse abstract
quando podem existir
métodos padrão usados como implementação default na superclasse.
Na herança estes métodos podem ser chamados
pelas classes concretas (que não o implementaram). Vejam
o exemplo dos métodos area()
e volume()
da classe Forma
.
A implementação da interface também obedece o relacionamento
é um
do conceito de herança.
Uma vantagem da interface sobre classes abstratas é que
uma classe pode implementar quantas interfaces ela precisar
e para tanto basta declarar as interfaces separadas por uma vírgula depois
da palavra implements
.
Vamos imaginar uma hierarquia em que duas categorias de funcionários estão subordinadas a um coordenador. Certamente este coordenador pode executar as mesmas tarefas executadas pelos seus subordinados (em POO são as ações ou métodos). Para uma implementação que tenta aproximar esta realidade ao código computacional poderíamos declarar a classe supervisor como um implementação das duas categorias de funcionários.
Vejamos os exemplos abaixo. Vamos analisar primeiramente uma interface
/** Shape */ public interface Forma { public abstract double area(); public abstract double volume(); public abstract String nome(); }
E agora duas classe que usam esta interface:
/** Ponto.java */ public class Ponto implements Forma { protected x, y; public Ponto () { x=y=0; } public Ponto (int xl, int yl) { xxl; y=yl; } public double area() { return 0; } public double volume() { return 0; } public nome() { return "Ponto"; } }
e...
/** Circulo.java */ public class Circulo extends Ponto { protected double raio; public Circulo() { raio=0; } public Circulo (double xraio, int xl, int yl) { super (xl, yl); raio = xraio; } public double area() { return Math.Pi*raio*raio; } public double volume() { return 0; } public nome() { return "Circulo"; } }
Façam um programa para testar este recurso de interface.
Até o momento praticamente sempre definimos uma classe
para cada arquivo .java
exclusivo e único.
No entanto, Java permite a definição de classe aninhadas
no corpo de uma outra classe.
Veja um exemplo de classe interna no código a seguir que implementa uma lista de um agregado de dados.
/** * Implementa no de uma lista * @version mar 2006 */ public class ListaTst extends Object{ //classe interna private class Infor extends Object { Object coisa; int ordem; public Infor (Object x) { coisa = x; ordem = numero(); } public String toString() { return("["+coisa+":(ordem) "+ordem+"]"); } }//Infor private Infor dado; private ListaTst prox; private static int i=0; ListaTst (Object entrada, ListaTst proximo) { dado = new Infor(entrada); prox = proximo; i++; } private int numero() { return i; } public String toString() { return(dado.toString()); } public void imprime() { if(prox != null) prox.imprime(); System.out.println(this); } }
E agora uma classe simples para testa as classes anteriores.
/** TListaTst.java */ public class TListaTst { public static void main(String[] jkl) { ListaTst ultimo; ultimo = null; for (int i=30; i<36; i++) ultimo = new ListaTst(new Integer(i), ultimo); ultimo.imprime(); } }
Uma das vantagens da implementação e uso das classes internas é que a informação contida na classe interna não precisa ser conhecida por outras classes. Isto é uma forma mais restritiva de encapsulamento de informação.
Notem que ao compilar as classes que contenham classes internas
são gerados arquivos .class
para as classes internas
além da classe nome do arquivo. Vejam que o nome destas
classes internas é precedido pelo nome da classe pública.
Os tipos primitivos são os tipos de dados usados para as construções
de TAD mais complexos. São os blocos fundamentais
dos TAD.
Java exige que todas as variáveis tenham tipos definidos antes
de serem usadas. Diz-se assim que Java é uma linguagem
fortemente tipada.
Em Java existe uma garantia que os tipos primitivos sempre terão a mesma representação interna, independentemente da plataforma de compilação ou mesmo de execução.
Outra informação importante é que quando os tipos primitivos
são declarados como atributos de uma classe são atribuídos valores
padronizados (default) para estas variáveis. Aos tipos
char
, byte
, short
,
int
, long
, float
e
double
é atribuído o valor zero por default. Ao
tipo
boolean
é atribuído o valor false
.
Veja também a tabela abaixo:
tipo | tamanho (bits) | valores |
boolean | 8 | true ou false |
char | 16 | '\u0000' a '\uFFFF' Unicode |
byte | 8 | -128 a +127 (-27 a 27-1) |
short | 16 | -32768 a + 32767 |
int | 32 | -231 a 231-1 |
long | 64 | -263 a 263-1 |
float | 32 | depende se intervalo + ou - |
double | 64 | depende se intervalo + ou - |
No entanto, a cada tipo de dado primitivo existe uma classe
invólucro
correspondente. Elas são as classes
Character
,
Byte
,
Short
,
Integer
,
Long
,
Float
,
Double
e
Boolean
.
As classes invólucro contém métodos que permitem a manipulação de tipos primitivos. Este processamento ocorre de maneira polimórfica. Vejam os seguintes exemplos nas API.
byteValue() compareTo() decode() equals() hashCode() toString() valueOf()
só para citar alguns deles.
Notem também que as classes numéricas de invólucro
herdam cacacterísticas da classe Number
.
Estudem esta classe para saber o porquê.
Qual é a definição de tipo desta classe?
Perceba também que cada classe invólucro é declarada
como final
logo, seus métodos também são
final e não podem ser sobrescritos. Ainda, notem que
muitos métodos são declarados como static
.
(evandro at usp ponto br)