Java

Wednesday, August 02, 2006

 

Generics [3]

Declarações válidas com generics:

ArrayList list1 = new ArrayList<String>();
List list2 = new ArrayList<String>();

List<String> list3 = new ArrayList<String>();
List<?> list4 = new ArrayList<String>();

Observe, no entanto, que a primeira e segunda declaracao nos permitirá fazer:

list2.add(new Integer(6));
list1.add(new Object());

E, por outro lado, a terceira declaração não captura nenhum tipo, portanto, não podemos nenhum objeto para os métodos de list, a não ser null.

list4.add("hello world"); //não compila.
list4.add(null); //compila, mas o seu efeito é nulo.

As seguintes declarações são inválidas, não compilam (suponha que Dog estende Animal):

List<?> list5 = new ArrayList<? extends String>();
List<? extends Dog> list6 = new ArrayList<Integer>();
List<? super Animal> list7 = new ArrayList<Dog>();

A primeira não compila, pois a inicialização precisa ter por base uma classe específica. A segunda não compila pois list6 é declarado para receber qualquer tipo que estenda Dog, mas Integer não estende Dog ea terceira não compila pois list7 é declarado para receber qualquer classe que seja superclasse de Animal, inclusive Animal, mas Dog é subclasse de Animal.

Vejamos mais algumas declarações válidas:

ArrayList<? extends String> list2 = new ArrayList<String>();
ArrayList<? extends CharSequence> list3 = new ArrayList<String>();
ArrayList<? super String> list4 = new ArrayList<CharSequence>();

Repare, no entanto, que não podemos fazer:

list2.add("Hello!");//nao compila
list3.add("Hello!");//nao compila

Pois list3 e list2 foram declarados para receber qualquer classe que estenda String e CharSequence, respectivamente, e, como já vimos, quando isto acontece, o método add fica bloqueado. No entando, podemos perfeitamente fazer

list4.add("Hello");

Saturday, July 08, 2006

 

Assertions

Assertions serve para que, durante o desenvolvimento do código, possamos verificar se certas condições supostas pela lógica do programa estão sendo satisfeitas. Por exemplo, suponha que um determinado método supostamente não recebe como argumento um inteiro negativo:

private void fazAlgo(int x){
assert(x>=0);
//fazAlgo
}

Se x não for maior ou igual a zero, uma exceção AssertionError será lançada. Essa exceção não deve ser capturada. Ela indica um erro na lõgica de programação.

Pode-se adicionar comentários que serão incorporados ao trace de erros.

private void fazAlgo(int x)}
assert(x>=0): "valor de x: "+x;
//fazalgo
}

A segunda expressão no assert pode ser qualquer objeto que será resolvido
em uma expressão String pelo método toString().

Assertions não devem ser usados em métodos públicos, salvo raras exceções.

Assertions foram incorporadas ao java a partir da versão 1.4. Antes disso, a palavra "assert" não era um keyword. Desta modo, era permitido que "assert" fosse um identificador. Para usar compiladores modernos >= java-1.4 sobre códigos antigos, que usam assert como identificador, devemos indicar ao compilador que se trata de um código antigo:

#javac -source 1.3 fonte_antigo.java

Caso contrário, haveria erro de compilação.

Asssertions devem ser habilitados ou desabilitados durante a chamada do progorama:

#java -ea Program

Podemos habilitar ou desabilitar seletivamente pacotes e classes:

#java -ea com.acme... Program //habilita assertions para todas as classes de acme em diante
#java -ea com.acme.Big Program //habilita assertions somente para as classes de Big
#java -da Program //desabilita assertions para Program (comportamento default)
#java -ea com.acme.Big -da Program //habilita assertions para as classes de Big, mas desabilita para Program

Friday, May 12, 2006

 

Tokens

Java nos fornece pelo menos três maneiras distintas de lidar com tokens. A classe String contém o método split(), por meio do qual dividimos um objeto String em tokens. Por default, o método split() usa o espaço em branco como delimitador, mas podemos passar como argumento qualquer expressão regex.

String s = "bla bla bla bla";
String token [] = s.split();

token receberá um vetor de tamanho quatro, contendo os tokens bla, bla, bla e bla.

String s = "a3 5aab5haf"
String token [] = s.split("\\d");

Agora usamos um dígito como delimitador. O resultado será também um vetor de tamanho quatro, contendo os tokens "a", " ", "aab", "haf".

A classe StringTokenizer nos fornece outra maneira de obter os tokens a partir de um objeto String. Ambas as construções abaixo são possíveis:

String s = "a3 5aab5haf"
StringTokenizer st = new StringTokenizer(s);
StringTokenizer st2 = new StringTokenizer(s, "\\d");

A primeira construção utiliza um delimitador padrão, que também é o espaço em branco. A segunda construção permite que se passe para o construtor o delimitador a ser utilizado. Para iterar entre os tokens, usamos o método hasMoreTokens(), para saber se há um novo token e nextToken() para obter o próximo token.

while (st.hasMoreTokens()){
System.out.println(st.nextToken());
}


A partir do java 1.5, temos mais uma maneira de lidar com tokens, fornecida pela classe Scanner. Seu construtor aceita tanto uma String quanto um InputStream ou mesmo um arquivo (File).

String s = "a3 5aab5haf"
Scanner scan = new Scanner(s);
File arq = new File("arquivo.txt");
scan = new Scanner(arq);

A classe Scanner fornece os métodos gerais hasNext() e next() para verificar se há mais tokens e retornar o próximo token, como um objeto String. Ela fornece também métodos mais especificos, como hasNextBoolean() ou hasNextInt() e os respectivos métodos de obtenção, nextBoolean(), nextInt() etc. Por default, a classe Scanner também usa o espaço em branco como delimitador.

Thursday, May 11, 2006

 

Regex

Para usar regex em java, nos valemos das classes Pattern e Matcher. Criamos uma expressão regular assim:

Pattern p = Pattern.compile("ab"); //onde "ab" é a expressão regular.

A String que servirá de base para a busca, criamos assim:

Matcher m = p.matcher("abaaaba");

Por fim, usamos os métodos find(), start(), group() de Matcher para obter as expressões capturadas pela expressão regular e o índice na string de base onde elas ocorrem.

boolean b = false;
while (b=m.find()){
System.out.print(m.start() + " "+m.group());//imprie o índice onde começa uma expressão capturada e o a própria expressão.
}

Metacaracteres relevantes:

\d -> digitos
\w -> caracteres alfanuméricos e underscore "_"
\s -> espaço
[abc] -> uma das letras a,b ou c
[a-f] -> uma letra em a e f
[a-fA-F] -> uma letra entre a e f, e A e F
+ -> uma ou mais ocorrências
* -> zero ou mais ocorrências
? -> zero ou uma ocorrência
. -> qualquer caractere
^ -> negação
*,+,? -> são interpretados gulosamente
*?,+?,?? -> são interpretados de maneira relutante
() -> Agrupa elementos

Exemplos:

padrão: .*xx
string: yyxxxyxx
Resultado: 0 yyxxxyxx

padrão: .*?xx
string: yyxxxyx
Resultado: 0 yyxx 4 xyxx

padrão: 0[xX]([0-9a-fA-F])+
string:12 0x 0x12 0xFF 0xg"
Resultado 6 0x12 11 0xFF

Um ponto importante a ser observado sobre os metacaracteres é que eles não podem ser atribuídos a uma string dessa maneira:

String qualquer="xxx\d";

Pois o compilador, ao encontrar \, vai procurar uma sequência de escape, no entanto, não existe a sequência de escape \d. Para que este erro de compilação não ocorra, o código deve ficar:

String qualquer="xx\\d"

Usamos uma contrabarra determinar que a próxima contrabarra deve ser lida literalmente.

Tuesday, May 09, 2006

 

Números, Datas e Formatação II

É por mei da classe Locale que obtemos facilidades para tratar a internacionalização. Para criar uma instância de Locale, temos basicamente duas opções:

Locale pt = new Locale("pt");
Locale ptBR = new Locale("pt","br");

Com o primeiro construtor obtemos um Locale relativo a uma língua, com o segundo, relativo a uma língua e a uma região onde essa língua é falada, o que é extremamente útil para tratar datas e valores em conformidade com os padrões de cada região. Por exemplo, o códgio a seguir

DateFormat dfbr = DateFormat.getDateInstance(DateFormat.FULL,ptBR);
Calendar c = Calendar.getInstance();
c.set(2006,05,09);
System.out.println(dfbr.format(c.getTime());

irá imprimir a data em sua forma mais extensa e em conformidade com os padrões brasileiros de apresentação de data, inclusive, traduzida para o português. Há mais dois métodos relevantes da classe Locale, por meio dos quais obtemos o nome da língua e do país representados por uma instância do Locale:

System.out.println(ptBR.getDisplayLanguage());
System.out.println(ptBR.getDisplayCountry());
System.out.println(ptBR.getDisplayLanguage(ptBR));
System.out.println(ptBR.getDisplayCountry(ptBR));

Os dois primeiros imprimem o nome da língua e do país no local default, que, geralmente, é o locale US. Neste caso, o resultado seria "portuguese" e "Brazil". Os dois últimos comandos imprimem no local passado como argumento, resultando, portanto, em "português" e "Brasil".

Por fim, a classe NumberFormat é usada para tratar números. Como DateFormat, ela é uma classe abstrata, portanto, para ter uma de suas instâncias, devemos fazer:

NumberFormat nf = NumberFormat.getInstance();
nf = NumberFormat.getNumberInstance();
nf = NumberFormat.getCurrencyInstance();

Cada um desses construtores é sobrecarregado para permitir o use de locales:

NumberFormat nf = NumberFormat.getInstance(ptBR);

Até o momento, não vi qualquer diferença entre getInstance() e getNumberInstance(), ambos retornam um formatador numérico de propósito geral, enquanto getCurrencyInstance(), um formator numérico para formatar valores financeiros. Sem o uso de locale, esses métodos retornam um formator orientado pelo locale default.
Um método importante dessa classe é setMaximumFractionDigits(), que serve para configurar o número de casas decimais após a vírgula na formação de um número de ponto flutuante. Por exemplo, se fizermos:

double d = 124.233213453;
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(3);
System.out.println(nf.format(d));

será impresso 124.233. NumberFormat também tem um método parse() e ele lança igualmente uma exceção caso a string passada não possa ser convertida para uma instância de Number.

Monday, May 08, 2006

 

Numeros, Datas e Formatação.

A classe Date está ultrapassada e deve-se usar Calendar para manipular datas. No entanto, ainda é últil usar,

Date data = new Date()

para obter uma representação do momento presente. A classe Calendar é abstrata, logo, não podemos criar uma instância dela diretamente com o comando,

Calendar calendario = new Calendar();

Devemos usar

Calendar calendario = Calendar.getInstance();

Podemos configurar a data atual representada por um objeto do tipo Calendar por meio de um objeto Date.

calendario.setTime(data);

Operações com datas são facilitadas pelos métodos roll() e add(). Para adicionar, à data atual, meses ou dias, fazemos:

calendario.add(Calendar.MONTH,1); \\será adicionado um mês à data atual.
calendario.add(DAY_OF_WEEK,3); \calendario.add(Calendar.HOUR,-5);\\a data atual será subtraída de 5 horas.
calendario.roll(Calendar.DAY_OF_WEEK,10);\\a data atual será acrescida de 10 dias.
\\no, entanto, se ela ultrapassar o limite do mês, este não será alterado.

A classe DateFormat provê dois métodos importantes para lidar com datas. format(Date d), que transforma um objeto do tipo Date em uma String e parse(String s) que transforma um objeto do tipo String em um objeto do tipo Date. Também a classe DateFormat é abstrata e, por isso, não pode ser instanciada diretamente. Ao invés, usamos:

DateFormat df = DateFormat.getInstance();
DateFormat df1 = DataFormat.getDateInstance();

A diferença entre ambos é que o método format() do primeiro irá produzir uma String que ela em consideração a data e a hora representados em um objeto Date, enquanto o segundo exprime apenas a data representada. O segundo método é sobrecarregado e permite que você passe como parâmetro um estilo para a formatação da data:

DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
df = DateFormat.getDateInstance(DateFormat.MEDIUM);
df = DateFormat.getDateInstance(DateFormat.LONG);
df = DateFormat.getDateInstance(DateFormat.FULL);

O método parse() lança a exceção ParseException e, por isso, só pode ser usado dentro da cláusula try com a captura da referida exceção. Se a String passada como argumento para o método parse() contém informações apenas sobre a data, mas não sobre a hora, então o objeto Date irá representar a data indicada e irá inicializar as horas e minutos como zerados.

Saturday, May 06, 2006

 

Construtores

Ao definir uma classe sem um construtor, o compilador irá definir implicitamente um construtor sem argumentos. Se, no entanto, você escreve um construtor, seja ele com ou sem argumentos, então o construtor não providencia nenhum construtor implicitamente. A primeira linha de um construtor deve ser super() ou this(). Se nenhum dos dois métodos é escrito explicitamente, o compilador irá inserir implicitamente super(). Ambas chamadas não podem ser declaradas no corpo do mesmo construtor, ou this(), ou super(). Repare, no entanto, que, em algum momento, haverá a chamada para o construtor da superclasse por meio do super(). Caso a superclasse não tenha um construtor sem argumentos, então você deverá chamar explicitamente, no construtor da classe herdeira, o construtor com argumentos da superclasse. Vejamos um exemplo:

public class Animal{
public Animal(String s){}
}

public class Dog extends Animal{}

O código acima não compila, pois o compilador irá gerar implicitamente um construtor na classe Dog que chama o construtor sem argumentos da superclasse por meio do super(), mas não há um construtor sem argumentos na classe Animal. Neste caso, o construtor de Dog deve ser explicitamente escrito e o construtor com argumentos da classe Animal chamado por meio de super(objeto_string).

Construtores não podem ter retornos e devem ter o mesmo nome da classe. O construtor providenciado pelo compilador, quando não há nenhum explícito na classe, tem o mesmo nível de acesso que a classe. Construtores podem ser declarados como private. Outro detalhe importante é que os métodos de uma instância só podem ser chamados depois que super() foi executados. O seguinte código, portanto, é ilegal:

public class Dog{
public Dog(){
eat();
this(5);
}
public Dog(int n){
}

public void eat();
}

O compilador irá adicionar implicitamente a chamada a super() no construtor com argumentos, mas o código não irá compilar ainda assim, pois no construtor sem argumentos há uma chamada a um método de instância, a saber, eat(), antes que ocorra a chamada de super. Antes do método super(), somente métodos estáticos podem ser chamados.

Archives

March 2006   April 2006   May 2006   July 2006   August 2006  

This page is powered by Blogger. Isn't yours?