Programação por Objectos Java Parte 7: Interfaces LEEC@IST Java – 1/31 Interfaces – revisão (1) • Uma interface é um conjunto de protótipos de métodos (sem implementações) que especifica um serviço bem definido: – As interfaces não podem conter atributos, mas podem conter constantes. – A implementação duma interface é realizada pela classe concreta. Na implementação ou concretização: • Determina-se os atributos necessários à correcta implementação da interface. • Descreve-se o código dos métodos das interface. LEEC@IST Java – 2/31 Interfaces – revisão (2) – Uma interface pode herdar as definições de outra interface. • Interfaces podem usar polimorfismo. • Se uma classe concretizar mais de uma interface, e várias interfaces contiverem métodos com a mesma assinatura, basta definir uma única implementação. – Uma interface não pode ser instanciada. LEEC@IST Java – 3/31 Interfaces (1) • • • • Tanto as classes como as interfaces definem tipos (a unidade fundamental da programação OO). Em Java, uma classe só pode estender uma outra classe, mas pode implementar uma ou mais interfaces. Para uma dada classe, as classes estendidas e as interfaces implementadas são denominadas supertipos. A classe em si é denominada o subtipo. Uma referência para um objecto do subtipo pode ser usada sempre que uma referência para um objecto dos seus supertipos (classes ou interfaces) é necessária. LEEC@IST Java – 4/31 Interfaces (2) • O Java disponibiliza um conjunto de interfaces, de onde se destaca: – Comparable: objectos deste tipo têm uma ordem associada que os permite comparar. – Iterable: objectos deste tipo disponibilizam um iterador, e portanto podem ser usadas no ciclo for-each. • Collection: objectos deste tipo conseguem guardar outros objectos. – Set, List, Queue, … LEEC@IST Java – 5/31 Interfaces (3) Sintaxe Qualif* interface Ident [ extends IdentI [, IdentI ]*] { [ QualifC* Tipo IdentC = expressão; ] * [ QualifM* Tipo IdentM ( [TipoP IdentP [, TipoP IdentP ]*); ]* } • Qualif: qualificador (visibilidade, entre outros) • Ident: identificador da interface • extends IdentI: especialização de interface LEEC@IST Java – 6/31 Interfaces (4) • Qualificadores de interface: – public: interface publicamente acessível. – abstract: a interface não pode ser instanciada. • Por omissão do qualificador public, uma interface é apenas acessível no pacote onde está definida. LEEC@IST Java – 7/31 Interfaces (5) • • • • • Todas as interfaces são implicitamente abstract. Por convenção, o qualificador abstract é omitido. Todos os membros são implicitamente public. Por convenção, o qualificador public é omitido. Todas as constantes são implicitamente public static final. Por convenção, os qualificadores são omitidos. Todos os métodos são implicitamente public abstract. Por convenção, os qualificadores são omitidos. Nenhum outro qualificador é permitido às contantes e métodos duma interface. LEEC@IST Java – 8/31 Interfaces (6) public interface Pilha { //métodos boolean vazia(); Object topo(); boolean adicionar(Object o); void remover(); } LEEC@IST Java – 9/31 Herança de interfaces (1) • • • • • Uma interface pode estender mais que uma interface. As interfaces que são estendidas são denominadas superinterfaces, enquanto que a nova interface é denominada subinterface. Uma subinterface herda todas as constantes declaradas nas suas superinterfaces. Se uma subinterface declara uma constante com o mesmo nome que uma constante herdada (independentemente do tipo), a constante da subinterface esconde a constante herdada. Na subinterface a constante herdada é acedida apenas pelo seu nome qualificado (superinterface.constante). LEEC@IST Java – 10/31 Herança de interfaces (2) interface X { int val = 1; String strx = “X”; } interface Y extends X { int val = 2; int sum = val + X.val; String stry = “Y extends” + strx; } LEEC@IST Java – 11/31 Herança de interfaces (3) • Se uma interface herda duas ou mais constantes com o mesmo nome, qualquer referência simples a essa constante é ambígua e resulta num erro de compilação. interface A { String str = “A”; } interface B extends A { String str = “B”; } interface D extends B, C { String d = str; } interface C extends A { String str = “C”; } interface D extends B, C { String d = A.str+B.str+C.str; } Erro de compilação: qual str? LEEC@IST Java – 12/31 Herança de interfaces (4) • • • Uma subinterface herda todos os métodos declarados nas suas superinterfaces. Se uma subinterface declara um método com a mesma assinatura, a menos de um retorno covariante, que um ou mais métodos herdados, o método da subinterface é uma redefinição dos métodos herdados. Se uma subinterface herda mais do que um método com a mesma assinatura, a menos de um retorno covariante, a subinterface contém apenas um método – o método que retorna o subtipo comum. LEEC@IST Java – 13/31 Herança de interfaces (5) • • Se um método da subinterface difere apenas no tipo de retorno de um método herdado, ou se dois métodos herdados diferem apenas no tipo de retorno, e estes retornos não são covariantes, há um erro de compilação. Se um método tem o mesmo nome, mas diferentes parâmetros que um método herdado, o método é uma sobreposição do método herdado. LEEC@IST Java – 14/31 Herança de interfaces (6) interface X { void xpto(); Number foo1(); Number foo2(); } interface Y { Object foo1(); Object foo2(); } interface Z extends X, Y { void xpto(String s); Integer foo1(); } • Métodos da interface Z: – – – – LEEC@IST public public public public void xpto() void xpto(String) Integer foo1() Number foo2() Java – 15/31 Implementação de interfaces (1) • Uma classe identifica as interfaces que implementa, listando-as a seguir à palavra chave implements. Sintaxe (revisão) Qualif* class Ident [ extends IdentC] [ implements IdentI [,IdentI]* ] { [ Decl_atributos | Métodos ]* } LEEC@IST Java – 16/31 Implementação de interfaces (2) • • As interfaces que uma classe implementa são denominadas as superinterfaces da classe. A classe deve disponibilizar uma implementação para todos os métodos definidos nas suas superinterfaces, senão a classe tem de ser declarada abstract. LEEC@IST Java – 17/31 Implementação de interfaces (3) • • Quando um classe implementa uma interface, a classe pode aceder às constantes definidas na interface como se tivessem sido declaradas na classe. Uma classe que implementa mais do que uma interface, ou que estende uma classe e implementa uma ou mais interfaces, sofre dos mesmo problemas de constantes escondidas e ambiguidade que uma interface que estende mais de uma interface (ver slides 10, 11 e 12). LEEC@IST Java – 18/31 Implementação de interfaces (4) interface X { int val = 1; String strx = “X”; } interface Y extends X { int val = 2; int sum = val + X.val; String stry = “Y extends ” + strx; } class Z implements Y { int val = 3; } Z z = new Z(); System.out.println( “z.val=” + z.val + “ ((Y)z).val=” + ((Y)z).val + /* ou Y.val */ “ ((X)z).val=” + ((X)z).val) /* ou X.val */; System.out.println(“z.strx=” + z.strx + “ z.stry=” + z.stry; No terminal é impresso z.val=3 ((Y)z).val=2 ((X)z).val=1 strx=X stry=Y extends X LEEC@IST Java – 19/31 Implementação de interfaces (5) interface A { String str = “A”; } interface B extends A { String str = “B”; } class D implements B, C { String d = str; } interface C extends A { String str = “C”; } class D implements B, C { String d = A.str+B.str+C.str; } Erro de compilação: qual str? LEEC@IST Java – 20/31 Implementação de interfaces (6) • • • Se uma classe implementa várias interfaces com mais do que um método com a mesma assinatura a classe contém apenas um tal método. Se uma classe implementa várias interfaces com mais do que um método com a mesma assinatura, a menos de um retorno covariante, a implementação deve definir o método que retorna o subtipo comum (caso contrário resulta num erro de compilação). Se uma classe implementa várias interfaces com mais do que um método que difere apenas no tipo de retorno, e estes retornos não são covariantes, há um erro de compilação. LEEC@IST Java – 21/31 Implementação de interfaces (7) interface X { void xpto(); Number foo1(); Number foo2(); } interface Z extends X, Y { void xpto(String s); Integer foo1(); } interface Y { Object foo1(); Object foo2(); } class ClasseZ implements Z { public void xpto() {...} public void xpto(String s) {...} public Integer foo1() {...} public Number foo2() {...} } É importante identificar os métodos a implementar de uma interface: se a ClasseZ não disponibilizar a implementação para todos os métodos definidos nas suas superinterfaces tem de ser declarada abstract (ver slide 17). LEEC@IST Java – 22/31 Implementação de interfaces (8) • A implementação da interface Pilha pode ser feita de diferentes maneiras: – Baseada numa tabela: PilhaComTabela – Baseada numa lista ligada: PilhaComLista • A interface Pilha corresponde ao tipo de dados abstracto, enquanto que ambas as classes PilhaComTabela e PilhaComLista correspondem ao tipo de dados implementado de duas formas diferentes. LEEC@IST public interface Pilha { //métodos boolean vazia(); Object topo(); boolean adicionar(Object obj); void remover(); } Java – 23/31 Implementação de interfaces (9) public class PilhaComTabela implements Pilha { private final int MAX; private Object pilha[]; private int posLivre; // primeira posição livre public PilhaComTabela(int max) { MAX = max; pilha = new Object[MAX]; posLivre = 0; } public boolean vazia() { return posLivre==0; } public Object topo() { return pilha[posLivre]; } LEEC@IST Java – 24/31 Implementação de interfaces (10) //continuação do slide anterior... public boolean adicionar(Object obj){ if (posLivre<MAX-1){ pilha[posLivre++] = obj; return true; } else return false; } public void remover() { pilha[posLivre--] = null; } public int numMaxElementos() { return MAX; } } LEEC@IST Java – 25/31 Implementação de interfaces (11) public class PilhaComLista implements Pilha { private ElementoPilha topo; private int numElementos; public PilhaComLista(){ topo = null; numElementos = 0; } public boolean vazia() { return numElementos==0; } public Object topo() { return topo!=null?topo.elemento:null; } LEEC@IST Java – 26/31 Implementação de interfaces (12) //continuação do slide anterior... public boolean adicionar(Object obj){ topo = new ElementoPilha(obj,topo); numElementos++; return true; } public void remover() { if (topo!=null) { topo = topo.próximo; numElementos--; } } public int numElementos() { return numElementos; } } LEEC@IST Java – 27/31 Implementação de interfaces (13) public class ElementoPilha { Object elemento; ElementoPilha próximo; public ElementoPilha(Object elem, ElementoPilha prox){ elemento = elem; próximo = prox; } } LEEC@IST Java – 28/31 Implementação de interfaces (14) • Definindo as interfaces um tipo, é possível declarar variáveis com o seu tipo: Pilha p = new PilhaComTabela(100); • Contudo, referências para um tipo de interface, só podem ser usadas para aceder a membros da própria interface: p.adicionar(new Integer(100)); p.adicionar(new Character(‘a’)); p.remover(); int max = p.numMaxElementos(); //INVÁLIDO!!! • Para contornar este problema pode usar-se um cast: int max = ((PilhaComTabela)p).numMaxElementos(); LEEC@IST Java – 29/31 Implementação de interfaces (15) • É possível chamar qualquer método de Object com uma referência para um tipo de interface: String s = p.toString(); LEEC@IST Java – 30/31 Implementação de interfaces (16) • O programador pode instanciar a pilha que mais lhe interessa: Pilha p1 = new PilhaComLista(); Pilha p2 = new PilhaComLista(); p1.adicionar(new Integer(5)); p2.adicionar(new Character(‘a’)); • Se houver interesse em usar PilhaComTabela, em vez de PilhaComLista, a única alteração reside na instanciação dos objectos, e não na declaração das referências para o tipo Pilha: Pilha p1 = new PilhaComTabela(20); Pilha p2 = new PilhaComTabela(100); p1.adicionar(new Integer(5)); p2.adicionar(new Character(‘a’)); LEEC@IST Java – 31/31