[Tens de ter uma conta e sessão iniciada para poderes visualizar esta imagem]
Em contraste com a estática dos dados, os métodos definem as ações a serem tomadas em diversos momentos da execução de um programa. Como em outras linguagens, como C, C++, Pascal, Fortran, etc, os métodos correspondem aos conceitos comuns de funções, procedimentos ou subrotinas. Estes são apenas conjuntos ordenados de declarações de dados, comandos e expressões. Em termos simples, são os métodos que realizam todas as tarefas para as quais o programa foi escrito, por exemplo, realizar cálculos, resumir informações de um arquivo, produzir um relatório, criar um gráfico, gerar um filme de animação, etc.
Classes
Os métodos, assim como os dados, têm um local de residência, as classes. Mais adiante, vamos estudar as classes em detalhes. Por hora, precisamos apenas de alguns poucos conceitos para poder entender os métodos. Pensemos uma classe como sendo um conjunto de dados (variáveis) e métodos (funções) da forma:
Código:
class [nome] {
[dados e métodos]
}
onde [nome] é um identificador que define o nome da classe, e o par de chaves delimita uma região para declaração de variáveis e métodos. A declaração de variáveis já foi vista anteriormente no capítulo sobre tipos de dados. Uma classe pode ser privativa ou pública. Uma classe privativa é declarada como no exemplo acima e é conhecida apenas no escopo delimitado pelo arquivo que a contém. Como um programa Java pode ser quebrado em múltiplos arquivos de código fonte distintos, pode ser necessário que as diversas partes integrantes do programa interajam, trocando dados entre si e chamando métodos umas das outras. Isso torna-se possível através das classes públicas, as quais são conhecidas por qualquer arquivo fonte que componha o programa. Para tornar uma classe pública, basta preceder sua declaração pela palavra-chave public como no seguinte exemplo:
Código:
public class [nome da classe] {
[dados e métodos]
}
Há uma convenção em Java que estabelece que deve haver exatamente uma classe pública para cada arquivo-fonte de que consiste um programa Java, e seu nome deve ser precisamente o nome do arquivo, sem o sufixo .java. Desse modo, existe uma correspondência biunívoca entre as classes públicas e os arquivos-fonte que as contém.
Podemos declarar uma classe a partir do chão, com todos os seus dados e métodos, ou podemos declarar uma classe derivando-a a partir de uma outra já existente. Este é um recurso muito útil, pois permite aproveitar um esforço de elaboração anterior, aumentando significativamente a produtividade da programação, e diminuindo o tamanho do código objeto. Suponhamos por exemplo, que tenhamos declarado previamente a seguinte classe:
Código:
class Polígono {
int cx, cy; // Coordenadas do centro do polígono
}
Esta classe define em linhas gerais o que é um polígono, guardando uma única característica comum a qualquer polígono, isto é, as coordenadas de seu centro. Agora, suponhamos que desejamos criar uma classe para guardar informações sobre um quadrado. Neste caso, não precisamos criar uma classe que dê as coordenadas do centro do quadrado assim como as suas dimensões. Basta fazer simplesmente:
Código:
class Quadrado extends Polígono {
int lado; // Comprimento de um lado do quadrado
}
A classe quadrado declarada desse modo se diz uma classe derivada da classe Poligono, da qual herda os dados (e os métodos) nela contidos. Esta declaração é equivalente a
class Quadrado {
int cx, cy; // Coordenadas do centro do polígono
int lado; // Comprimento de um lado do quadrado
}
Desejando fazer uma classe para representar um retângulo, bastaria fazer então
Código:
class Retângulo extends Polígono {
int base, alt; // Base e altura do retângulo
}
Objetos
Uma particular instância de uma classe é chamada objeto. Para entender a diferença entre classes e objetos, fazemos alusão à metáfora da fábrica de torradeiras. A fábrica de torradeiras não é uma torradeira, mas define o tipo de produto que sai dela, isto é, as torradeiras. Do mesmo modo a torradeira não é a fábrica, mas o produto feito por ela. Comparamos as classes às fabricas e os objetos aos produtos feitos por elas. Grosso modo, podemos dizer que as classes não ocupam espaço na memória, por serem abstrações, equanto que, os objetos ocupam espaço de memória por serem concretizações dessas abstrações.
Nas declarações acima, introduzimos algumas classes que permitem representar polígonos. Porém, não instanciamos nenhuma das classes criando particulares objetos a partir dessas classes. Por exemplo, a partir da classe quadrado, podemos fazer objetos representando quadrados de diversos comprimentos laterais, ou retângulos de diferentes dimensões:
Código:
Quadrado A, B, C;
Retângulo D;
A.lado = 1; // O quadrado A terá os lados de comprimento 1
B.lado = 2; // O quadrado B terá os lados de comprimento 2
C.lado = 7; // O quadrado C terá os lados de comprimento 7
D.base = 3; // O retangulo D terá base 3 e ...
D.alt = 4; // altura 4, e centrado na origem
D.cx = 0;
D.cy = 0;
As declarações acima são semelhantes às que vimos no capítulo anterior, com excessão de que no lugar do nome que identifica o tipo de dado estamos usando o nome de uma classe. Neste exemplo, as classes Quadrado e Retângulo foram empregadas para declarar os objetos (ou variáveis) A, B, C e D.
Em certo sentido as classes complementam os tipos de dados nativos da linguagem Java, com tipos de dados complexos criados pelo programador. Esse fato, aliado à possibilidade de derivar classes, tornam as linguagens orientadas a objetos extremamente producentes.
Chamando métodos
Um método entra em ação no momento em que é chamado. Isto pode ocorrer explicitamente ou implicitamente. A chamada explícita se dá por ordem do programador através da execução de um comando ou expressão contendo o nome do método. Em nosso programa AloPessoal fizemos uma chamada explícita do método System.out.println para mostrar um texto na tela do computador. As chamadas implícitas ocorrem quando o interpretador Java chama um método por sua própria deliberação. A chamada do método main é um exemplo de chamada impícita. O interpretador Java chama esse método para dar início à execução do programa.
Declarando métodos
A declaração mais simples que podemos fazer de um método (lembrando que isso deve ser feito dentro de uma classe) é a seguinte:
Código:
void [nome do método] () {
[corpo do método]
}
onde o [nome do método] é um identificador que define o nome pelo qual o método é conhecido, e [corpo do método] consiste de uma lista ordenada de eclaração de variáveis, de expressões e de comandos. A primeira palavra-chave, void, define o valor retornado pelo método, neste caso, nenhum. Podemos usar qualquer tipo de dado válido como valor de retorno de um método. Nesse caso, ao terminar, o método seria obrigado a devolver um dado do tipo especificado. Por exemplo,
Código:
class Numero {
double x = 1;
void print() {
System.out.println("O valor e " + x);
}
}
define uma classe chamada Numero, a qual contém uma variável x, inicializada com 1, e um método sem valor de retorno, print, que apenas escreve um texto e o valor de x, através da chamada do método System.out.println.
O par de parênteses adiante do nome do método introduz uma lista (vazia, neste caso) de argumentos. A chamada de um método pode ser acrescida de parâmetros, os quais são associados aos seus respectivos argumentos.
Um exemplo de métodos que retornam valores é o seguinte:
Código:
class Calculador {
int Soma(int a, int b) {
return a + b;
}
double Produto(double a, double b) {
return a * b;
}
}
O primeiro método, Soma, realiza a adição de de dois números inteiros fornecidos pelos argumentos a e b, devolve a soma valor de retorno. O segundo método realiza a multiplicação de dois números de ponto-flutuante a e b devolvendo seu produto.
A sintaxe completa para a declaração de um método é a seguinte:
Código:
[moderadores de acesso] [modificador] [tipo do valor de retorno] [nome] ([argumentos])
throws [lista de excessões]
{ [corpo]
}
onde os termos em itálico são opcionais (ou acessórios). Neste capítulo, vamos analisar detalhadamente cada um dos termos [moderadores de acesso], [modificador], [tipo do valor de retorno], e [argumentos]. Vamos, porém, adiar um pouco as explicações sobre [lista de excessões] até o capítulo sobre excessões. Uma excessão à esta sintaxe é a que se aplica aos métodos especiais, chamados construtores, que serão vistos adiante no capítulo sobre classes.
Moderadores de Acesso
Os moderadores de acesso são empregados para restringir o acesso a um método. Entretanto, independentemente do moderador escolhido, um método é sempre acessível, isto é, pode ser chamado, a partir de qualquer outro método contido na mesma classe. Os moderadores de acesso existentes em Java são os seguintes:
public: O método declarado com este moderador é público e pode ser chamado a partir de métodos contidos em qualquer outra classe. Esta é a condição de menor restrição possível.
protected: O método é protegido pode ser chamado por todas as classes que compõe um conjunto maior chamado package. Veremos adiante que as packages são um modo conveniente de organizar diversas classes que estão em estreito relacionamento.
Obs: é importante avisar que você pode ter problemas em identificar violações com respeito à chamada de métodos protegidos. Isso se deve ao fato do compilador não sinalizar esse fato precisamente, isto é, a tentativa de chamar um método protegido a partir de uma classe que não faz parte do package. Ao invés disso a mensagem poderá se parecer com a seguinte:
No method matching funcao() found in class Matematica
friendly: Uso do método é permitido dentro da classe que o contém, assim como dentro de qualquer classe que tenha sido derivada dessa classe, ainda que esteja fora do package. Este é o moderador default, isto é, aquele que é assumido se nenhum moderador for explicitamente especificado.
private: O método é privativo da classe que o contém e seu uso é vedado a qualquer outra classe.
private protected: o método é acessível pela classe que o contém, assim como por qualquer classe que tenha sido derivada dessa classe. Porém, isso somente é permitido apenas dentro de um mesmo arquivo-fonte.
Exemplo:
Código:
// classe de numero
class numero {
double x=1;
public void print1() {
System.out.println("O valor e "+x);
}
void print2() {
System.out.println("O valor e "+x);
}
}
// classe principal
public class PrintNum {
public static void main(String args[]) {
numero num = new numero();
num.print1(); // correto
num.print2(); // ilegal
}
}
O exemplo acima dará um erro, pois não pode acessar o print2. O método print1 é definido como public e portanto, está disponível a qualquer classe, mas o print2 não tem especificação (logo, é assumido como friendly) e portanto, a classe principal PrintNum não pode acessa-lo.
Modificador do Método
O modificador do método permite especificar algumas propriedades de um método, determinando como classes derivadas podem ou não redefinir ou alterar o método, e de que forma esse método será visível.
static: o método é compartilhado por todos os objetos instanciados a partir da mesma classe. Um método static não pode acessar qualquer variável declarada dentro de uma classe (salvo se a variável estiver declarada também como static, o que veremos mais adiante), pois não é capaz de dicernir entre os diferentes objetos que compartilham esse método.
abstract: Podemos declarar um método sem contudo especificar seu corpo, dizendo-o abstrato. Isto funciona como uma espécie de lembrete para que alguma classe derivada complete a declaração fornecendo um corpo. Assim sendo, uma classe que contenha um método abstrato, ou que seja derivada de alguma classe que contenha um método abstrato mas não complete sua declaração, não pode ser instanciada. O uso da palavra-chave abstract é opcional, isto é, as duas declarações seguintes são equivalentes:
abstract void print();
void print();
final: Especifica que nenhuma classe derivada pode alterar ou redefinir este método. Um método declarado como final deve ser obrigatóriamente seguido de um corpo.
native: Declara apenas o cabeçalho sem corpo, como no caso de abstract. Porém, esta especificação designa um método que implementado em outra linguagem como C++, por exemplo.
synchronized: Esta declaração é usado para desenvolver o programa de processamento concorrente. Seu propósito é impedir que dois métodos executando concorrentemente acessem os dados de uma classe ao mesmo tempo. synchromized especifica que se um método estiver acessando o dado, outros que também desejem acessá-lo têm que esperar.
Tipo de Valor de Retorno:
O tipo de valor de retorno é especificado por uma palavra chave ou nome de classe na declaração do método, estabelecendo o valor que ele pode devolver.
Código:
// Classe de números complexos
class Complexo {
double x, y; // parte real e complexo, respectivamnte
public double Re() { // retorna a parte real
return x;
}
public double Im() { /retorna a parte imaginaria
return y;
}
public Complexo Vezes(Complexo c) {
Complexo resultado;
resultado.x = x * c.x - y * c.y;
resultado.y = x * c.y + y * c.x;
return resultado;
}
public void print() {
System.out.println("(" + x + " + " + y + "i)");
}
}
public class Teste {
public static void main(String args[]) {
Complexo z, w; z.x = 1;
z.y = 2;
System.out.print( "O valor de z é ");
z.print();
System.out.println( "A parte real de z é = " + z.Re() );
System.out.println( "A parte imaginária de z é = ", z.Im() );
System.out.print("O valor de z ao quadrado é ");
w = z.Vezes( z );
w.print();
}
}
Ao executar esse programa teríamos a resposta:
O valor de z é (1 + 2i)
A parte real de z é = 1
A parte imaginária de z é = 2
O valor de z ao quardado é (-3 + 4i)
Um método que retorna valor, isto é, não declarado como void, deve conter a linha return ...; a qual especifica o valor a ser retornado. Por exemplo, return x; especifica que o valor da variável x será retornado.
Lista de Argumentos:
A lista de argumentos é a lista de valores que o método vai precisar, obdecendo a sintaxe
[tipo 1] [nome 1], [tipo 2] [nome 2], ...
onde [tipo ?] é a especificação do tipo de dado e [nome ?] é um identificador pelo qual o parâmetro é conhecido.
Exemplo:
Código:
// Classe de numeros complexos
class Complexo {
double x=1, y=1; /* parte real e complexo, respectivamnte
public double Re() { // retorna a parte real
return x;
}
public double Im() { /retorna a parte imaginaria
return y;
}
public set(double a, b){
x = a;
y = b;
}
}
public class Teste {
public static void main(String args[]) {
Complexo c = new Complexo();
c.set(3,2);
System.out.println("z = (" + c.Re() + " + " + c.Im() + "i)");
}
}
uma observação importante é que Java trata o objeto por referência e por isso, se o método modificar o objeto recebido como parâmetro, o objeto será modificado externamente. No entando, se o parâmetro recebido for tipo simples (int, double, char, float, etc), visto no tipo de dados, o método pode alterar o parâmetro a vontade que não influencia no valor externo.
Para ilustrar, veja o exemplo a seguir:
Código:
// classe de inteiro
class inteiro {
public int i;
}
// classe principal
public class TestPar {
// método que altera o valor do parametro int
// este metodo deve ser static, pois sera chamado
// pelo metodo main() que e static
static void MudaInt(int k) {
System.out.println("MudaInt: valor de k e " + k);
k += 2;
System.out.println("MudaInt: valor de k e apos incremento e " + k);
}
// método que altera o valor do parametro inteiro
// este metodo deve ser static, pois sera chamado
// pelo metodo main() que e static
static void MudaInteiro(inteiro k) {
System.out.println("MudaInteiro: valor de k e " + k.i);
k.i += 2;
System.out.println("MudaInteiro: valor de k e apos incremento e " + k.i);
}
// main() do TestPar
public static void main(String args[]) {
int i;
inteiro n = new inteiro();
i = 2;
n.i = 3;
System.out.println("Valores originais:");
System.out.println("Valor de i e "+i);
System.out.println("Valor de n e "+n.i);
MudaInt(i);
MudaInteiro(n);
System.out.println("Valores apos a chamada dos metodos:"):
System.out.println("Valor de i e "+i);
System.out.println("Valor de n e "+n.i);
}
}
A especificação public de uma variável dentro da classe, faz com que este variável seja acessado de qualquer lugar. Para especificar a variável i do objeto k, basta escrever k.i. Em geral, a variável [var] dentro do objeto [obj] do tipo classe e referenciado por [obj].[var]. Note o uso de especificação static para os métodos MudaInt() e MudaInteiro(). Esta especificação é necessaria, pois o método principal é static, e método static não pode chamar outros métodos da mesma classe que não seja static.
O exemplo acima fornece a saída:
Valores originais:
Valor de i e 2
Valor de n e 3
Valores apos a chamada dos metodos:
Valor de i e 2
Valor de n e 5