Arquivos compactos e método main em Java
A partir do JDK 21, foi introduzida, como recurso experimental, a possibilidade de criar arquivos compactos e métodos main simplificados, reduzindo a quantidade de código necessária para escrever programas Java. No JDK 25, esse recurso deixou de ser experimental e passou a estar disponível de forma estável.
O objetivo é simplificar tanto a estrutura do arquivo-fonte quanto a definição do método main. Com isso, é possível escrever programas sem declarar explicitamente uma classe e, ao mesmo tempo, usar um formato reduzido para o main.
Método main compacto
Tradicionalmente, um programa Java precisava seguir a seguinte estrutura, por exemplo, no arquivo Program.java:
public class Program {
public static void main(String[] args) {
System.out.println("Hello Programício!");
}
}Antes, para criar e executar um programa, era necessário:
- Declarar uma classe (
public class Program) - Criar um método
mainestático com parâmetroString[] args(public static void main(String[] args))
Com o novo recurso, é possível definir o main sem public static e sem o parâmetro String[] args:
class Program {
void main() {
System.out.println("Hello Programício!");
}
}Nas versões JDK 21, 22, 23 e 24, esse formato era experimental e exigia ativar o modo de pré-visualização ao compilar e executar. Por exemplo, no JDK 22:
javac --release 22 --enable-preview Program.java java --enable-preview Program
A partir do JDK 25, essas opções adicionais não são mais necessárias:
javac Program.java java Program Hello Programício!
O processo de escolha e execução do método main funciona assim:
- Se a classe definir ou herdar um método
maincom parâmetroString[], o Java Launcher seleciona esse método. - Caso contrário, se houver um método
mainsem parâmetros, ele será escolhido. - Se nenhum método adequado for encontrado, ocorre erro e a execução é encerrada.
Quando o método escolhido é estático, ele é chamado diretamente. Se for de instância, a classe precisa ter um construtor público ou de pacote, sem parâmetros. Nesse caso, o launcher cria uma instância e chama o método main nessa instância. Se esse construtor não existir, ocorre erro.
Exemplo com dois métodos main não estáticos:
public class Program {
void main(String[] args) {
System.out.println("With args");
}
void main() {
System.out.println("Without args");
}
}Como existe uma versão com String[] args, ela será a escolhida para execução.
Arquivos compactos
Se o compilador encontrar campos e métodos no arquivo que não estejam dentro de uma declaração de classe, ele cria automaticamente uma classe de nível superior para conter esse conteúdo. Esse tipo de arquivo é chamado de arquivo-fonte compacto.
Antes, era obrigatório ter algo como:
public class Program {
public static void main(String[] args) {
System.out.println("Hello Programício!");
}
}Agora é possível escrever:
void main() {
System.out.println("Hello Programício!");
}Para arquivos compactos, o compilador gera implicitamente:
- Uma classe no pacote não nomeado, estendendo
java.lang.Object - Um construtor padrão sem parâmetros
- Os campos e métodos declarados no arquivo como membros dessa classe
- Um método
mainobrigatório; caso contrário, erro de compilação
Podemos incluir outros métodos e campos no arquivo, que também serão membros dessa classe implícita:
String sayHello() {
return "Hello";
}
void main(String[] args) {
System.out.println(sayHello());
}Ou com campos:
String message = "Hello Work";
void main(String[] args) {
System.out.println(message);
}Como a classe é implícita, ela não possui um nome utilizável no código-fonte. O nome gerado pelo compilador é interno e não deve ser usado. Não é possível instanciá-la com new, mas é possível acessar o objeto atual com this.
Declarando outros tipos no mesmo arquivo
Podemos definir outros tipos, como classes, dentro do mesmo arquivo compacto:
void main(String[] args) {
Person tom = new Person("Tom");
System.out.println(tom.getName());
}
class Person {
private String name;
Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}Nesse caso, Person se comporta como se estivesse declarada dentro da classe implícita do arquivo.
Se preferir, é possível declarar explicitamente uma classe para conter o main e deixar outros tipos fora dela:
class Program {
void main(String[] args) {
Person bob = new Person("Bob");
System.out.println(bob.getName());
}
}
class Person {
private String name;
Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}Resumo
- O JDK 25 tornou estáveis os arquivos compactos e o método
mainsimplificado, antes experimentais no JDK 21 a 24. - É possível definir um método
mainsempublic statice sem parâmetroString[] args. - Arquivos compactos permitem escrever código sem declarar explicitamente uma classe, gerando uma classe implícita automaticamente.
- Esses arquivos podem conter campos, métodos e outros tipos, como classes internas ou externas ao
main. - No JDK 25, não é necessário usar opções de pré-visualização para compilar e executar programas com essas funcionalidades.