Este texto foi atualizado e doado ao projeto Code Design World. Leia a versão mais atualizada. |
Polimorfismo
Polimorfismo é uma característica importante em qualquer linguagem, especialmente se a linguagem é orientada a objetos e especificamente em Java. O polimorfismo é muitas vezes confundido com o próprio conceito de orientação a objetos, mas embora dependa dele em certa medida, é, na realidade, um conceito separado.
O polimorfismo está presente de diferentes maneiras. Estas são aquelas relevantes para Java mas quase todas se aplicam a outras linguagens.
Variável Polimórfica
Este tipo de polimorfismo permite atribuir objetos de tipos (classes, interfaces …) diferentes a uma mesma variável. Para que isto seja possível deve existir uma relação de herança entre o tipo da variável e o tipo do objeto, de tal modo que o tipo da variável seja igual ou mais abstrato que o tipo do objeto.
Este tipo do polimorfismo é na realidade uma forma de encapsulamento que, por sua vez, é uma concretização do Princípio de Separação de Responsabilidade; e é necessária para uma linguagem orientada a objeto completa.
No exemplo a seguir a variável texto não é polimórfica já que String é uma classe final. Não é possível estender String e, portanto, não é possivel criar tipos menos abstratos que String.
|
|
Código 1: |
No exemplo seguinte, a varável texto é polimórfica já que CharSequence é uma interface, o que , por definição, significa é possivel que exista um conjunto de possibilidades para os objetos que podem ser atribuidos a essa variável.
|
|
Código 2: |
Utilizar interfaces e classes abstratas como os tipos das variáveis garante explicitamente que a variável é polimórfica. Utilizar classes abstratas e sobretudo interfaces como tipos de variáveis é considerado uma boa prática e é conhecido como “programar para interfaces”. Na realidade, isto apenas significa :”utilize variáveis polimórficas por padrão e sempre que possível”
Sombreamento
Sombreamento (shadowing) é a capacidade de poder definir duas, ou mais, variáveis com o mesmo nome em escopos diferentes. O código a seguir apresenta o exemplo clássico:
|
|
Código 3: |
O sobreamento permite que o mesmo nome seja utilizado para duas variávels diferentes. No caso, a variável “nome” definida na classe e a variável “nome” definida no método.
O detalhe com o uso de sobreamento é que as variáveis de maior escopo podem interagir com as de menor escopo. Contudo, como elas têm o mesmo nome, é necessário destingui-las. Para isso, é utilizada a palavra reservada this que representa o objeto corrente e contém, portanto, variáveis de escopo de classe. Caso o this não fosse utilizado junto de ‘nome’, o compilador assume que você está se referindo à variável de menor escopo; no caso a definida no método. Isso não é uma falha. É a utilidade do sobreamento.
O compilador Java é uma tanto esperto e avisa o programador de falhas básicas. Uma delas é a tentativa de atribuir uma variável a ela própria. Isso é um código que não tem nenhum propósito e o compilador o avisará quando detectar essa situação. Por isso se você escrever o código seguinte:
|
|
Código 4: |
O compilador apresentará um aviso na linha 6 dizendo que a variável está sendo atribuída a ela própria. Isso demonstra que o compilador escolhe sempre a variável de menor escopo.
Sobrecarga
Sobrecarga (overload) é a capacidade de poder definir dois, ou mais métodos, numa mesma classe com o mesmo nome. Para que exista sobecarga não é necessário que a linguagem seja orientada a objetos e, por isso, à semelhança do sombreamento a sobrecarga é normalmente entendida com uma característica da linguagem e não como uma forma de polimorfismo.
Embora os métodos possam ter o mesmo nome, eles têm obrigatoriamente que ter uma assinatura diferente. Eis alguns exemplos:
|
|
Código 5: |
Sobre-escrita
Sobre-escrita (overriding) é a capacidade de poder redefinir a implementação de um método que já foi definido e implementado em uma classe superior na hierarquia de herança.
Para que exista sobre-escrita é necessário que o método seja definido com a exata assinatura que existe na classe superior.
|
|
Código 6: |
O método calculaSoma em SomadorInteligente sobre-escreve o método calculaSoma em Somador redefinindo a logica de soma.
Tipo Genérico
Tipos genéricos permitem estabelecer relações fortes entre os tipos, mas sem especificar o tipo real que será utilizado. Tipos genéricos são uma inovação recente da linguagem Java mas já conhecidos e utilizados em linguagems anteriores.
Esta forma de parametrização possibilita que classe sejam criadas utilizando uma outra classe ou grupo de classes sem necessidade de fazer casting explícito e possibilitando maior controle sobre o funcionamento da classe. Tipo genérico é uma parametrização do tipo e portanto é utilizando em qualquer lugar onde tipo é usado. Por exemplo, na definição de uma variável.
|
|
Código 7: |
Tipo generico, à semelhança da variável polimorfica, é também um forma de encapsulamento.
Auto-boxing e Auto-unboxing
Java suporta tipos primitivos (ou seja, tipos de variáveis que não são objetos). Em algumas situações é necessário converter esses valores primitivos para objetos. Isso é conhecido como boxing (colocar em caixas). O processo inverso é chamado unboxing (retirar das caixas). Auto-boxing e Auto-umboxing acontece quando próprio compilador faz essa operação. Este recurso foi adicionado à linguagem Java a partir da sua versão 5.
Numero de argumentos indefinido (Var args)
Algumas vezes é útil passar vários argumentos de um certo tipo para um método. Normalmente isso é feito pela utilização de arrays ou coleções. Contudo isso não é prático. O ideal seria passar os argumentos como se fossem argumentos individuais e capturá-los depois como um array. Essa funcionalidade conhecida como var args é uma forma de polimorfismo já que a forma do programador invocar o método é diferente da forma com que ele trabalha o resultado, contudo os dados são os mesmos.
|
|
Código 8: |
Categorias de Polimorfismo
Alguns dos tipos de polimorfismo escondem o real funcionamento do programa (por serem forma de encapsulamento). A única forma de saber exatamente o que está acontecendo é analisar o programa enquanto está funcionando. Estes tipos de polimorfismo formam uma categoria designada: Polimorfismo Dinâmico.
Os outros tipos cujo efeito no programa é claro mesmo quando o programa não está funcionando, ou seja, pode ser compreendido diretamente da análise do código compõem a categoria designada: Polimorfismo Estático.
Muitas das funcionalidades do polimorfismo estático não dependem do conceito de objeto e podem ser encontrados em outras linguagens. Talvez por isso não seja comum encontrar referência a essas capacidades como tipos de polimorfismo e são normalmente apresentadas como funcionalidades da linguagem. Em contrapartida, as funcionalidades de polimorfismo dinâmico dependem, quase todas, dos conceitos de objeto e herança. Muitas vezes elas se confundem com os próprios conceitos de herança e orientação a objetos e são normalmente apresentadas como parte integrante desse paradigma.
Polimorfismo Dinâmico
Eis um sumário da características integrantes do polimorfismo dinâmico:
- Variáveis Polimórficas
- Tipo genérico
Polimorfismo Estático
Eis um sumário da caracteristicas integrantes do polimorfismo estático:
- Sobrecarga
- Sobre-escrita
- Sombreamento
- Auto-(un)boxing
- Var args
Licença
Sérgio Taborda Este trabalho é licenciado sob a Licença Creative Commons Atribuição-Uso Não-Comercial-Não a obras derivadas 3.0 Genérica . |
Ola Sergio
la no comeco tem um new StringBuffer em vez de new StringBuilder…
e essa frase “Para que exista sobre-escrita é necessário que o método seja definido com a exata assinatura que existe na classe superior” nao é totalmente correta, mas creio que voce nao quis deixar complicada, nao eh?
Obrigado Paulo pela dica, já arrumei o artigo. Quanto à sua citação não entendi porque “nao é totalmente correta”. Só para deixar claro: se não for a mesma assinatura não é sobre-escrita, é sobrecarga. Será que era isso que estava a pensar ?
Nao, nao foi isso nao. Eh que tem algumas coisas que pode mudar sim e continuar sobreescrita, como as exceptions lancadas e o nivel de visibilidade (private/package/protected/public).
Entendi seu ponto, mas assinatura de um método não inclui exceções nem níveis de visibilidade, nem detalhes de implementação como final, synchronized, etc..
A assinatura do método é apenas o seu nome e o conjunto ordenado de parâmetros.
Isso em que você está pensando chama-se a declaração do método, não a assinatura.
(http://www.jguru.com/faq/view.jsp?EID=15776)