Já sabemos que as classes e os interfaces definem tipos.
Se uma classe
C
implementa um interfaceI
, dizemos que
C
é subtipo deI
,- logo
I
é supertipo deC
.
A noção de subtipo está na base das regras usadas pelo compilador para atribuição de valores a variáveis de tipos não primitivos (instruções de atribuição e instanciação de parâmetros aquando de invocação de métodos):
- numa instrução de atribuição
T x = expressao
o tipo deexpressao
tem que ser subtipo (que inclui ele próprio) deT
;- numa invocação
m(expressao)
de um método cuja assinatura éT1 m(T2 param)
, o tipo deexpressao
tem que ser subtipo (que inclui ele próprio) deT2
;
Logo, as instruções seguintes são corretas, pois o tipo das referências à direita das duas primeiras atribuições é subtipo do tipo das variáveis à esquerda dessas atribuições:
C x = new C();
I y = new C();
Mais adiante voltaremos a este assunto para que compreenda as razões que justificam estas regras.
Considere o interface I
definido na secção anterior. Suponhamos agora que existem
A
que implementa I
, ou seja, que implementa os métodos m1
e m2
, e que acrescenta um outro método m3
;B
que implementa I
e que implementa um novo método m4
.A figura seguinte representa um diagrama em UML das classes e interface e das suas relações.
Numa qualquer classe cliente das classes acima, podemos declarar variáveis às quais atribuímos referências para objetos dos tipos A
e B
e invocar métodos sobre esses objetos:
xpublic class ExemploCliente {
public static void main (String [] args) {
A x = new A();
I y = new A();
I[] vetorIs = new I[3];
vetorIs[0] = new A();
vetorIs[1] = new B();
vetorIs[2] = new A();
int soma = x.m1() + y.m1();
x.m2();
y.m2();
if (x.m3()){
System.out.println(y.toString());
}
for (int i = 0 ; i < vetorIs.length ; i++){
soma += vetorIs[i].m1();
}
System.out.println(soma);
vetorIs[0].m2();
vetorIs[1].m2();
}
}
Repare que:
x
tem tipo declarado A
, podemos invocar sobre ela todos os métodos acessíveis em A
: m1
, m2
, m3
;y
tem tipo declarado I
, podemos invocar sobre ela todos os métodos acessíveis em I
: m1
, m2
; embora o objeto referenciado por y
tenha o tipo real A
, não é possível invocar sobre ele o método m3
, pois não está definido em I
nem em Object
;vetorIs
tem tipo declarado I[]
, então todos os seus elementos têm tipo declarado I
; logo, podemos invocar sobre cada elemento de vetorIs
todos os métodos acessíveis em I
: m1
, m2
; embora o objeto referenciado por vetorIs[0]
tenha o tipo real A
, não é possível invocar sobre ele o método m3
, pois não está definido em I
; o mesmo se passa com vetorIs[1]
: o objeto que refere tem o tipo real B
, mas não é possível invocar sobre ele o método m4
, pois não está definido em I
.
Anterior: 13.2. Representação em UML
Seguinte: 13.4. O interface Ganhavel