Tópicos de Poo e Java
- segunda aula -

Neste módulo veremos:

Interfaces

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.

Quando devemos usar interface ou superclasse abstrata?

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.

Classes internas

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.

Invólucros para tipos primitivos

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.

Última modificação feita em: 12 de outubro de 2008
Evandro Eduardo Seron Ruiz, PhD (evandro at usp ponto br)