sexta-feira, setembro 10, 2010

Automatic Resource Management - Parte 2

Joe Darcy indicou hoje que conseguiu convencer o pessoal que cuida do JDBC 4.1 a aderir ao "try-with-resources". ( Project Coin: JDBC 4.1 and try-with-resources )

Portanto, será possível você fechar automaticamente uma conexão ou um ResultSet, por exemplo, usando um simples "try". Isso ainda não está disponível no build 109 do JDK 7 (o mais recente), mas estará em versões futuras.

(Não posto ainda um exemplo; quando o build que implementar o JDBC 4.1 estiver disponivel, irei atualizar este post.)

segunda-feira, agosto 16, 2010

Java 7: Automatic Resource Management

A partir do Java 7 build 105 (disponível em http://download.java.net/jdk7/ ) é possível escrever código que feche automaticamente alguns tipos de recursos, como arquivos. A classe deve implementar a interface AutoCloseable.
Para a especificação, consulte http://blogs.sun.com/darcy/entry/project_coin_updated_arm_spec
Exemplo:
import java.io.*;

class TesteTryWithResources {
    public static void main (String[] args) {
        // Exemplo simples
        try (PrintWriter pw = new PrintWriter ("test.txt")) {
            pw.println ("Hello, world!");
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }

        // Exemplo um pouco mais complexo, que
        // envolve 2 arquivos
        try (BufferedOutputStream bos = new BufferedOutputStream (
                new FileOutputStream ("copy.of.file"));
             BufferedInputStream bis = new BufferedInputStream (
                new FileInputStream ("original.file"))) {
            byte[] buffer = new byte[10240];
            for (int nBytes = bis.read (buffer); nBytes > 0;
                    nBytes = bis.read (buffer)) {
                bos.write (buffer, 0, nBytes); 
            }
        } catch (final FileNotFoundException | IOException ex) {
            ex.printStackTrace();
        }
    }
}

quarta-feira, junho 09, 2010

Tratamento de exceções - Java 7

No Java 7 será possível tratar vários tipos de exceções em um mesmo bloco "catch". Isso elimina algum código repetitivo. Vou dar um exemplo com o método "verify" de java.security.cert.Certificate, que lança "apenas" 5 tipos de exceções. O método verify1 abaixo está codificado da maneira antiga (uma cláusula catch para cada tipo). O método verify2 está codificado à la Java 7, ou seja, as exceções podem ser agrupadas usando-se a sintaxe:

catch (final Excecao1 | Excecao2 | Excecao3 ... | ExcecaoN ex)

O motivo de se exigir "final" é que a variável ex tem um tipo que é da superclasse de todas as exceções citadas, e essa variável não pode ser alterada, por exemplo, atribuindo-se a ela uma exceção de uma outra classe qualquer. Em vez disso, para simplificar, os projetistas da linguagem escolheram impor que se usasse "final".

import java.security.PublicKey;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;

class TesteExcecoes { 
// Código pré-Java 7 
public static void verify1 () {
        Certificate cert = null;
        PublicKey key = null;
        
        try {
            cert.verify (key);
        } catch (CertificateException ex) {
            System.out.println ("Certificado inválido");
        } catch (NoSuchAlgorithmException ex) {
            System.out.println ("Não pode ocorrer");
        } catch (InvalidKeyException ex) {
            System.out.println ("Erro na chave ou na assinatura");
        } catch (NoSuchProviderException ex) {
            System.out.println ("Não pode ocorrer");
        } catch (SignatureException ex) {
            System.out.println ("Erro na chave ou na assinatura");
        }
    } 
// Código Java 7 - note o uso de "final" e "|" 
public static void verify2 () {
        Certificate cert = null;
        PublicKey key = null;
        try {
            cert.verify (key);
        } catch (CertificateException ex) {
            System.out.println ("Certificado inválido");
        } catch (final NoSuchAlgorithmException | NoSuchProviderException ex) {
            System.out.println ("Não pode ocorrer");
        } catch (final InvalidKeyException | SignatureException ex) {
            System.out.println ("Erro na chave ou na assinatura");
        }
    }
    public static void main (String[] args) {
        verify1();
        verify2();
    }
}

Usei o build 95 do Java 7. Pegue o build mais recente, disponível em http://download.java.net/jdk7/ .

terça-feira, março 09, 2010

Switch com Strings no Java 7

Uma coisa que existe em quase todas as linguagens, exceto Java e C++, é a facilidade de você usar um "switch" com o tipo String. Isso será adicionado ao Java 7.
Peço permissão ao Entanglement para postar um programa que ele mostrou no GUJ:
class TesteSwitch {
    public static void main (String[] args) {
        String s = "desprezível";
        switch (s) {
        case "desprezível":
            System.out.println ("despicable"); break;
        case "auréola":
            System.out.println ("halo"); break;
        case "fungo":
            System.out.println ("fungus"); break;
        case "fujão":
            System.out.println ("fugitive"); break;
        case "abstruso":
            System.out.println ("abstruse"); break;
        }
    }
}
Sendo que esse programa foi transformado pelo compilador para o equivalente a:
class TesteSwitch {
    public static void main (String[] args) {
        String s = "desprezível";
        int hashCode = s.hashCode();
        int i = -1;
        switch (hashCode) {
        case -644512679: if (s.equals ("auréola")) i = 1; 
                         else if (s.equals("desprezível") i = 0; 
                         break;
        case 97793703: if (s.equals ("fujão") i = 3; 
                       else if (s.equals("fungo") i = 2; 
                       break; 
        case 1732918561: if (s.equals ("abstruso") i = 4; 
                         break;
        default: break;
        }
        switch (i) {
        case 0: System.out.println ("despicable"); break;
        case 1: System.out.println ("halo"); break;
        case 2: System.out.println ("fungus"); break;
        case 3: System.out.println ("fugitive"); break;
        case 4: System.out.println ("abstruse"); break;
        }
    }
}

O compilador transforma 1 switch em 2, porque é mais fácil tratar de certos casos (como "fallthrough" e strings que têm exatamente o mesmo hash code). Por exemplo, no caso acima, "auréola" e "desprezível" têm o mesmo hash code, assim como "fujão" e "fungo". E é por isso que o switch checa se o hash code E a string são os mesmos do caso. Se o switch tiver muitos casos, essa implementação é mais rápida que simplesmente ir comparando com "ifs" encadeados.

Obviamente, assim como no caso do "switch" com inteiros, é necessário ver se o seu problema não poderia ser resolvido com hierarquias de classes ou então com enums.

Usando literais binários e separadores de milhar em Java 7

Em Java 7, poderemos usar números binários (embora sejam um pouco extensos), o que facilita a quem precisa codificar máscaras de bits.

Além disso, uma coisa bem mais útil é que podemos agora pôr separadores de dígitos dentro de constantes numéricas.

É bem mais fácil conferir pi = 3.141_592_653_589_793_238_462_643_383_279_5 em vez de 3.1415926535897932384626433832795.

Entretanto, esse suporte a separadores de milhar vale só para o compilador; as rotinas (como Integer.parseInt ou DecimalFormat.parse) não entendem o "_". Isso, provavelmente, é para garantir compatibilidade com programas já existentes.

Uma coisa que sempre me incomodou é que números octais são iniciados por um simples zero, tal como em C/C++. Isso é muito confuso; seria preferível que houvesse um indicador de base (como, digamos, a letra Q. Por exemplo, em vez de 0377, teríamos a constante 0Q377.). Como isso já é uma "pegadinha" antiga da linguagem, eu sugiro que se use sempre um separador, para você tomar cuidado com esse zero inicial. Em vez de usar o número 0377, deveríamos usar 0_377. Mas não sei se uma convenção dessas ajudaria muito ou não.

import java.text.*;

// Requer Java 7 para ser compilada
class NovosLiterais {
    public static void main (String[] args) {
        int cafeBabe = -889275714;
        assert (cafeBabe == 0xCAFEBABE); // hexadecimal
        assert (cafeBabe == 031277535276); // octal
        // 0b introduz um número em notação binária
        assert (cafeBabe == 0b11001010111111101011101010111110); // binário
        // "_" separa os dígitos.
        assert (cafeBabe == 0b1100_1010_1111_1110_1011_1010_1011_1110); // binário
        // Pode-se usar separadores de dígitos em qualquer base
        assert (cafeBabe == 0xCAFE_BABE);
        assert (cafeBabe == -889_275_714);
        assert (cafeBabe == 0_31_277_535_276);
        // Não é preciso pôr os separadores de 3 em 3 ou de 4 em 4. 
        long cnpj = 65_497_745_0001_53L;
        // É possível usar separadores de dígitos mesmo com números de ponto-flutuante
        double pi = 3.141_592_653_589_793_238_462_643_383_279_5;
        double avogadro_constant = 6.022_141_79E+23;
        // Você não pode usar o "_" no início de uma constante, porque aí teríamos
        // um identificador. Ele só pode ser usado dentro de uma constante.
        double _42; // isto é um identificador, não uma constante
        // .parseInt, .parseLong não suportam "_"
 try { 
            long x = Long.parseLong ("CAFEBABE", 16); 
            x = Long.parseLong ("CAFE_BABE", 16);
        } catch (NumberFormatException ex) { ex.printStackTrace(); }
 try { 
            int x = Integer.parseInt ("11001010", 2); 
            x = Integer.parseInt ("1100_1010", 2);
        } catch (NumberFormatException ex) { ex.printStackTrace(); }
        // DecimalFormat.parse também não suporta "_"
        NumberFormat df = DecimalFormat.getInstance();
        try { Number n = df.parse ("123_456"); 
            System.out.println (n); // imprime "123", o que indica que ele não consegue ver além do "_"
        }  catch (ParseException ex) { ex.printStackTrace(); }
    }
}

sexta-feira, março 05, 2010

Usando <> em Java 7

Em Java 7 é possível agora abreviarmos a chamada a um construtor se o tipo tiver parâmetros.
Em vez de termos de repetir todos os tipos parametrizados, é suficiente usarmos <>. Na documentação isso é chamado de "diamond" (losango), em alusão à forma do novo operador.

// Requer Java 7
import java.util.*;

class TesteNotacaoAbreviada {
    public static void listMap (Map<String, String> map) {
        for (Map.Entry<String, String> entry : map.entrySet()) {
        }
    }
    public static void main (String[] args) {
        // Note que não é necessário escrever "new HashMap<String, String>()"
        Map<String, String> traducao = new HashMap<>();        // Nem aqui é preciso escrever "new TreeMap<String, String>()"
        traducao = new TreeMap<>();
        // A notação abreviada funciona também para passagem de parâmetros, mas
        // é bem mais difícil fazê-la funcionar adequadamente. No caso abaixo, 
        // o compilador acha que o tipo que está sendo passado é new TreeMap<Object,Object>
        // e emite um erro de compilação. 
        listMap (new TreeMap<>());
    }
}

Da série "Para eu não me esquecer mais..."

Estou tendo de usar o Compuware DevPartner Studio 9.0.1 (versão de demonstração) para resolver uns problemas de desempenho em um programa em C++, e sempre me esqueço que é necessário arranjar um arquivo chamado wcore-4.9.6658.0.zip, abri-lo, copiar o arquivo wcore.dll para o diretório C:\Program Files\Common Files\Compuware\NMShared\4.9 e reiniciar os serviços do DevPartner.

quinta-feira, março 04, 2010

Identificadores com espaços no Java 7

Se você baixou uma versão recente do JDK 7 (veja http://download.java.net/jdk7 ), pode tentar compilar o seguinte programa.

class #"Teste Identificadores Com Espacos" {
    public static void main (#"String"[] #"argumentos de linha de comando") {
        System.#"out".#"println" ("Os argumentos de linha de comando passados para este programa são:");
        for (#"String" #"argumento" : #"argumentos de linha de comando") {
            System.out.printf ("%s %n", argumento); 
        }
    }
}

Isso foi introduzido pelo John Rose, um pouco "por debaixo dos panos" (já que ele é um dos committers do Sun JDK), e comunicado "after the fact" em: http://wikis.sun.com/display/mlvm/ProjectCoinProposal.
Quem se lembra daqueles identificadores de colunas no SQL (que são cercados por aspas, se contiverem caracteres especiais ou espaços) vai imediatamente reconhecer a utilidade de tais identificadores malucos. O funcionamento é mais ou menos o mesmo: se houver um caracter que não for letra, número, cifrão ou "underscore" em um identificador, você pode "forçar a barra" usando o #"".
Você pode até forçar um pouco mais a barra, e criar uma classe com um nome que é uma palavra reservada do Java.

class #"int" {
}

Só que os seguintes caracteres ainda não são aceitos*, porque são importantes para a JVM distinguir pacotes, nomes de classes, arrays de objetos, e métodos construtores:
. / ; [ ] < >
* Segundo a especificação, bastaria usar um "\\" antes deles, mas o javac não deixa usar esses caracteres mesmo com "\\". Pois é...

terça-feira, janeiro 12, 2010

A classe java.util.BitSet

A classe java.util.BitSet é muito útil para alguns problemas que exigem você saber se um determinado elemento está presente ou não com rapidez e eficiência de memória.
Em vez de usar um byte ou 4 bytes para cada elemento booleano (como você poderia esperar em um boolean[] ou então em um ArrayList), BitSet usa apenas um bit.
Infelizmente, java.util.BitSet não implementa java.util.Set nem java.util.List. Mas ela tem métodos bem interessantes, como cardinality (que conta quantos elementos "true" existem no conjunto, mas de maneira muito eficiente), nextSetBit e nextClearBit (que procuram qual é o próximo elemento "true" ou "false" respectivamente. Em algumas versões da JVM, esses métodos também são implementados com instruções especiais do processador.
Vou dar um exemplo de uso dessa classe, implementando o Crivo de Eratóstenes (para achar os números primos). Para simplificar, não vou efetuar a otimização de indicar no crivo apenas os números ímpares.


import java.util.*;
class Sieve {
private BitSet bs;
public void findPrimes (int n) {
bs = new BitSet (n+1);
int sq = (int) Math.sqrt (n);
// Ao final desta rotina, todos os bits marcados serão primos, e os
// bits desmarcados serão números compostos.
bs.set (0, n - 1, true); // marcando todos os bits como primos...
// Agora vamos achar o primeiro bit primo, e desmarcar todos os bits
// que são múltiplos dele. Vamos começar por 3
int x = 2;
while (x <= sq) {
for (int i = x + x; i <= n; i += x) {
bs.clear (i);
}
// Devemos achar o próximo bit setado que seja maior que x
x = bs.nextSetBit (x + 1);
}
}
public boolean isPrime (int n) {
return bs.get (n);
}
public int nextPrime (int n) {
return bs.nextSetBit (n + 1); // -1 se não houver um próximo
}
public long sumPrimes () {
int n = 1;
long sum = 0;
while ((n = bs.nextSetBit (n + 1)) > 0) {
sum += n;
}
return sum;
}
public static void main(String[] args) {
Sieve s = new Sieve();
s.findPrimes (10);
System.out.println (s.sumPrimes());
s.findPrimes (100);
System.out.println (s.sumPrimes());
s.findPrimes (2000000);
System.out.println (s.sumPrimes());
long n = 600851475147L; // = 3 * 7^2 * 11 * 163 * 2279657
int sqrtN = (int) Math.sqrt (n);

int factor = 2;
int nFactors = 0;
while (n > 1) {
int exponent = 0;
while (n % factor == 0) {
exponent++;
n /= factor;
}
if (exponent != 0) {
System.out.printf ("%d^%d . ", factor, exponent);
nFactors++;
}
factor = s.nextPrime (factor);
if (factor == -1) {
if (n != 1)
System.out.printf ("%d^%d . ", n, 1);
break;
}
}

System.out.println ();
System.out.println (nFactors);
}
}