• Nenhum resultado encontrado

Em linguagens orientadas a objetos, as classes são as construções protagonistas do paradigma. Classes, assim chamadas no estrito sentido de classificação, são a estrutura fundamental da linguagem para a criação de entidades e distribuição de responsabilidades.

Além disso, uma classe é uma interface bem definida para a execução de determinadas operações, em que as particularidades da implementação são, por assim dizer, omitidas

quando a classe é consumida. O termo instância é usado para designar a criação de uma variável com o tipo correspondente a uma classe.

Uma classe se potencializa com o conceito de encapsulamento. Essa noção in-troduz a possibilidade de restringir a visibilidade de membros da classe, como métodos (essencialmente funções) e variáveis definidas no escopo da classe. O encapsulamento se dá, de modo geral, pelos seguintes modificadores de acesso:

• Privado: método ou variável visível apenas dentro do escopo da classe. Isto é, instâncias não podem acessar membros definidos como privados.

• Protegido: membro da classe visível apenas nos escopos da própria classe e de classes herdeiras.

• Público: métodos e variáveis públicos são acessíveis pelo escopo da própria classe, de classes herdeiras e a partir de uma instância dessa classe.

Código 2.1: Exemplo de classe e modificadores de acesso usando a linguagem Kotlin.

1 class C a l c u l a t o r {

O Código 2.1 mostra um breve exemplo sobre encapsulamento. A classeCalculator define2métodos (funções):sum, método público que mapeia dois números inteiros para um número real, que representa a soma de ambos, ecastT oDouble, método privado que converte um inteiro em um número real. A funçãomain, na linha12, cria uma instância da classe Calculator, chamada dec. Em seguida, na linha 13, é impresso na saída pa-drão a soma entre os números inteiros5 e2 pelo uso do método públicosum, definido na classeCalculator. Entretanto, a invocação (chamada) ao métodocastT oDouble, na linha14, implica em erro, pois esse método não é acessível pela instância por ser privado.

A herança de classes é outro conceito determinante nas linguagens orientadas a objetos. Pragmaticamente, se a classeD herda da classe C, então D absorve todos os

métodos e variáveis de classe públicas e protegidas definidas emC. Ademais, a nível de sistema de tipos, seo é uma instância de objeto do tipo D, então pode-se afirmar que a avaliação do tipo deoé verdadeira tanto quando testada paraDquanto para o tipoC, i.e., oé do tipoDe, por herança,oé do tipoC também.

Código 2.2: Exemplo de herança na linguagem Kotlin.

1 class C {

No exemplo ilustrado pelo Código 2.2, a classe Dherda da classeC. Nesse pro-cesso, D irá incorporar à sua própria estrutura os métodos m1 e m2, respectivamente público e protegido. Por definição, o métodom3, privado, não será assimilado pela classe D.

Outra capacidade oportuna de linguagens orientadas a objetos é o conceito de classe abstrata. Em essência, uma classe abstrataAnão pode ser instanciada: ela é defi-nida apenas para que outras classes a estendam, isto é, herdem deA. Tal facilidade per-mite que conceitos por demais abstratos sejam modelados em classes não instanciáveis, mas, ao mesmo tempo, abre a possibilidade para a especialização desses mesmos con-ceitos por classes que os tornem palatáveis pela instanciação. Mais do que isso: métodos também podem ser abstratos, devendo obrigatoriamente ser implementados por quaisquer classes não abstratas que derivem, por exemplo,A.

Código 2.3: Classe abstrata escrita em Kotlin.

1 a b s t r a c t class A {

O Código 2.3 ilustra o conceito de classe abstrata. A classe B herda da classe Ae é forçada, por não ser também abstrata, a descrever a implementação do métodom, abstrato na definição dada porA. Além disso, pela herança anteriormente discutida, B

passa a contar com o método públicof.

O último aspecto de interesse pertinente a linguagens OO é o polimorfismo para-métrico, o qual é comumente chamado em linguagens como Kotlin e Java de genéricos.

Esse recurso foi introduzido como uma forma de manter o comportamento de uma classe sem depender necessariamente de um tipo, de modo que se mantenha a conveniência da tipagem estática, contribuindo para a consistência de um programa e reduzindo substan-cialmente a possiblidade de erros.

Código 2.4: Estrutura de dados Pilha sem o uso de polimorfismo paramétrico.

1 class AStack {

O Código 2.4 ilustra o tipo abstrato de dados Pilha implementado em Kotlin sem o advento do polimorfismo paramétrico. As operações elementares de uma pilha, i.e., inserir (método push) e remover / recuperar (pop), são feitas sobre objetos em que os tipos não são garantidos. O tipoAny, de fato, aceita qualquer tipo de instância de objeto;

é similar aojava.lang.Objectda linguagem Java. Logo, a operação depopda pilha deve ser seguida de uma coerção (cast) de tipo, notada nas linhas17e18damaindefinida no Código 2.4. Entretanto, como não há garantia de tipos, a coerção da linha18falha apenas em tempo de execução, pois a pilha contém apenas objetosInt(linhas15-16).

Código 2.5: Estrutura de dados Pilha com polimorfimo paramétrico.

1 class AStack <T> {

Essa situação é trivialmente contornada pelo polimorfismo paramétrico. O Código 2.5 demonstra como a pilha pode ser implementada com polimorfismo paramétrico e, em especial, deixa claro o poder desse recurso. A notação< T > adicionada ao lado do nome da classe indica que a declaração de uma instância deAStacksó será completa se o tipo for definido, como mostra a linha13. Dessa forma, todas as operações de inserção e remoção de elementos da pilha são seguras a nível de tipo, logo, a tentativa de recuperar um elemento da pilha como umaString, conforme tenta a linha17, incorre em erro em tempo de compilação, reforçando ainda mais o sistema de tipos e a confiabilidade da execução do programa ao detectar um erro em tempo de análise semântica.