[Prévia] [Próxima] [Prévia por assunto] [Próxima por assunto]
[Índice cronológico] [Índice de assunto]

Re: [off topic] - Re: Exceções , interfaces e Java



On Wed, Apr 17 2002 at 07:43:20pm -0300, Jorge F. Del Teglia wrote:
> 
> [...] é bom que a relação entre classes chamada 
> herança cumpra o principio de sustitutabilidade (Barbara Liskov) [...]
> 
> Eu, pessoalmente, acredito que modelos de classes onde a propriedade 
> anterior é verdade, são mais "sãos".

Legal, mas parece-me que voce ainda esta' enxergando classes e tipos como
sinonimos; eles so' sao sinonimos em C++ e em Java. A relacao de heranca
tende "naturalmente" a permitir a "substitutabilidade", mas nao e' a unica
forma de permiti-la. Por exemplo:

class A {
public:
	void printnum (int i) { cout << i << endl;};
}

class B {
public:
	void printnum (int i) { cout << i << endl;};
}

Em smalltalk, classes com essa "cara" poderiam ser usadas uma no lugar da
outra; em C++/Java, nao. Agora, ponha a mao na consciencia e responda:
qual comportamento e' mais coerente? Parece-me que e' o do smalltak,
porque *de fato* as duas classes correspondem ao mesmo tipo. Mas em C++
e em Java, a unica forma de permitir a "substitutabilidade" e' atraves da
heranca: isso e' uma limitacao da linguagem, que trata tipos e classes
como sinonimos. Essa limitacao existe para beneficio da checagem estatica
de tipos e da performance.


> > >Em Java e C++: dois objetos que tem exatamente a mesma interface so'
> > >sao do mesmo tipo se tambem pertencem a uma mesma hierarquia de
> > >classes.
> 
> Por exemplo, em Java:
> 
> interface M1
> {
>         void m1();
> }
> 
> class A
>         implements M1
> {
>         public void m1() {}
> }
> 
> class B
>         implements M1
> {
>         public void m1() {}
> }
> 
> Neste caso, instâncias de A e de B, tem exatamente a mesma interface.
> São de mesmo tipo.  Mas não pertencem a uma mesma hierarquia de classes
> (deixemos fora a java.lang.Object)

Mas e' ai' que esta' o ponto: a sintaxe de Java "esconde" de voce o fato
que, na verdade, A e B pertencem, sim, a uma mesma hierarquia de classes,
definida pela classe puramente abstrata M1, que e' "pai" de A e B. Java
quer vender a ideia que interfaces sao melhores/diferentes que a heranca
com uma classe pai puramente abstrata de C++, mas isso simplesmente nao e'
verdade: sao duas sintaxes que correspondem *exatamente* `a mesma
implementacao, que e' a mesma que e' usada no caso da heranca.

Vejamos: imaginemos uma pequena modificacao na linguagem Java: vamos
eliminar a checagem de tipos. Como Java nao permite que uma fonte
"externa" ao programa modifique suas estruturas internas (ou seja,
enquanto um programa Java esta' em execucao eu nao posso mudar o valor de
uma variavel se essa operacao nao se der atraves de um pedaco do proprio
codigo), e' obvio que todas as atribuicoes e chamadas de metodos acontecem
com objetos de tipos conhecidos em tempo de compilacao (bem diferente de
smalltalk). Portanto, se em algum lugar do programa existe algo do tipo:

object.method()

O compilador tem como saber quais os tipos possiveis de objetos definidos
pela variavel object e pode, portanto, verificar, em tempo de compilacao,
se o metodo "method" existe para todos eles. Portanto, o compilador
poderia garantir que nao ha' chamadas invalidas no codigo sem precisar de
tipagem estrita. As classes A e B do *meu* exemplo acima poderiam ser
usadas uma no lugar da outra e tudo iria funcionar beleza. Por que, entao,
essa paranoia toda com os tipos?

O problema e' que o compilador teria que gerar codigo para lidar com cada
um desses objetos ao longo de todo o programa, o que aumentaria, no
minimo, o consumo de memoria do programa. Isso porque voce nao tem um
jeito facil de encontrar "aonde esta'" o metodo "method" para todos os
objetos que podem ir parar nesse lugar.

O truque para resolver o problema e': quando voce define uma superclasse,
voce esta' definindo uma tabela de apontadores para funcoes ou para
apontadores para funcoes que correspondem aos metodos daquela classe. Para
todos os objetos de uma mesma classe, quando o codigo "chama" um metodo, o
compilador sabe que basta des-referenciar um ponteiro para chegar ao
metodo "correto". Ja' com classes diferentes, nao haiveria um jeito
eficiente de chamar o metodo (uma das opcoes e' vasculhar todos os metodos
do objeto, que e' o que smalltalk faz). Assim, "fazer de conta" que
classes e tipos sao sinonimos traz um ganho razoavel de performance em
tempo de execucao.

Muito bem; entao, o que acontece quando voce "implements" uma interface de
Java? Exatamente a mesma coisa: voce define a localizacao dentro do objeto
da tabela de apontadores para os metodos daquela interface. Por isso e'
que voce precisa das interfaces: para garantir a performance de execucao.
Mas se voce olhar bem vai ver que esse processo e' o processo de heranca
de interface de C++ (e tambem o processo de heranca "normal" em Java). Por
isso, a sintaxe e' diferente, mas "implementar uma interface Java" e'
herdar de uma interface.

Em resumo: MAC440 e' legal :-)))

Ate' +
Nelson

P.S. - O Alan comentou uma vez que em Java, o que acontece de fato e' um
processo semelhante ao do smalltalk, ou seja, na chamada "object.method()"
a JVM procura pela implementacao do metodo em todas as superclasses da
classe do objeto. Mas imagino que as JVM's mais novas sejam "espertas" o
suficiente para operar da mesma maneira que os compiladores C++ fazem (que
e' o que eu descrevi ai'), pelo menos na maioria dos casos. Na pior das
hipoteses, quando Java e' compilada para linguagem de maquina (por
exemplo, com o gcj), o compilador deve se aproveitar desse mecanismo.