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
main
está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
main
com parâmetroString[]
, o Java Launcher seleciona esse método. - Caso contrário, se houver um método
main
sem 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
main
obrigató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
main
simplificado, antes experimentais no JDK 21 a 24. - É possível definir um método
main
sempublic static
e 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.