Sintaxe Java - Java syntax
A sintaxe de Java se refere ao conjunto de regras que definem como um programa Java é escrito e interpretado.
A sintaxe é derivada principalmente de C e C ++ . Ao contrário de C ++, em Java não existem funções ou variáveis globais, mas existem membros de dados que também são considerados como variáveis globais . Todo código pertence a classes e todos os valores são objetos . A única exceção são os tipos primitivos , que não são representados por uma instância de classe por motivos de desempenho (embora possam ser convertidos automaticamente em objetos e vice-versa por autoboxing ). Alguns recursos, como sobrecarga de operador ou tipos inteiros sem sinal, são omitidos para simplificar a linguagem e evitar possíveis erros de programação.
A sintaxe Java foi gradualmente estendida no curso de vários lançamentos principais do JDK e agora oferece suporte a recursos como programação genérica e literais de função (chamados de expressões lambda em Java). Desde 2017, uma nova versão do JDK é lançada duas vezes por ano, com cada lançamento trazendo melhorias incrementais para o idioma.
Fundamentos
Identificador
Um identificador é o nome de um elemento no código . Existem certas convenções de nomenclatura padrão a serem seguidas ao selecionar nomes para elementos. Os identificadores em Java fazem distinção entre maiúsculas e minúsculas .
Um identificador pode conter:
- Qualquer caractere Unicode que seja uma letra (incluindo letras numéricas como algarismos romanos ) ou dígito.
- Sinal de moeda (como ¥).
- Caractere de pontuação de conexão (como _ ).
Um identificador não pode:
- Comece com um dígito.
- Ser igual a uma palavra-chave reservada, literal nulo ou literal booleano.
Palavras-chave
resumo | Prosseguir | para | novo | trocar |
afirmar | predefinição | vamos para | pacote | sincronizado |
boleano | Faz | E se | privado | isto |
pausa | Duplo | implementos | protegido | lançar |
byte | outro | importar | público | arremessa |
caso | enum | instancia de | Retorna | transitório |
pegar | estende | int | baixo | Experimente |
Caracteres | final | interface | estático | var |
classe | finalmente | grande | strictfp | vazio |
const | flutuador | nativo | super | volátil |
enquanto |
Literais
Inteiros | |
---|---|
binário (introduzido no Java SE 7) | 0b11110101 ( 0b seguido por um número binário) |
octal | 0365 ( 0 seguido por um número octal) |
hexadecimal | 0xF5 ( 0x seguido por um número hexadecimal) |
decimal | 245 (número decimal) |
Valores de ponto flutuante | |
flutuador | 23.5F , .5f , 1.72E3F (fração decimal com um indicador de expoente opcional, seguido por F ) |
0x.5FP0F , 0x.5P-6f ( 0x seguido por uma fração hexadecimal com um indicador de expoente obrigatório e um sufixo F ) | |
Duplo | 23,5D , 0,5 , 1,72E3D (fração decimal com um indicador de expoente opcional, seguido por D opcional ) |
0x.5FP0 , 0x.5P-6D ( 0x seguido por uma fração hexadecimal com um indicador de expoente obrigatório e um sufixo opcional D ) | |
Literais de caracteres | |
Caracteres | 'a' , 'Z' , '\ u0231' (caractere ou escape de caractere, entre aspas simples) |
Literais booleanos | |
boleano | verdadeiro , falso |
literal nulo | |
referência nula | nulo |
Literais de string | |
Fragmento | "Hello, World" (sequência de caracteres e escapes de caracteres entre aspas duplas) |
Caracteres escapam em strings | |
Caractere Unicode | \ u3876 ( \ u seguido pelo ponto de código Unicode hexadecimal até U + FFFF) |
Fuga octal | \ 352 (número octal não superior a 377, precedido por barra invertida) |
Alimentação de linha | \ n |
Retorno de carruagem | \ r |
Feed de formulário | \ f |
Barra invertida | \\ |
Citação única | \ ' |
Citação dupla | \ " |
Aba | \ t |
Backspace | \ b |
Literais inteiros são do int
tipo por padrão, a menos que o long
tipo seja especificado anexando L
ou l
sufixo ao literal, por exemplo 367L
. Desde o Java SE 7, é possível incluir sublinhados entre os dígitos de um número para aumentar a legibilidade; por exemplo, um número 145608987 pode ser escrito como 145_608_987 .
Variáveis
Variáveis são identificadores associados a valores. Eles são declarados escrevendo o tipo e o nome da variável e, opcionalmente, são inicializados na mesma instrução atribuindo um valor.
int count; //Declaring an uninitialized variable called 'count', of type 'int'
count = 35; //Initializing the variable
int count = 35; //Declaring and initializing the variable at the same time
Várias variáveis do mesmo tipo podem ser declaradas e inicializadas em uma instrução usando a vírgula como delimitador.
int a, b; //Declaring multiple variables of the same type
int a = 2, b = 3; //Declaring and initializing multiple variables of the same type
Desde o Java 10, tornou-se possível inferir tipos para as variáveis automaticamente usando var
.
// stream will have the FileOutputStream type as inferred from its initializer
var stream = new FileOutputStream("file.txt");
// An equivalent declaration with an explicit type
FileOutputStream stream = new FileOutputStream("file.txt");
Blocos de código
Os separadores { e} significa um bloco de código e um novo escopo. Os membros da classe e o corpo de um método são exemplos do que pode viver dentro dessas chaves em vários contextos.
Dentro dos corpos dos métodos, colchetes podem ser usados para criar novos escopos, da seguinte maneira:
void doSomething() {
int a;
{
int b;
a = 1;
}
a = 2;
b = 3; // Illegal because the variable b is declared in an inner scope..
}
Comentários
Java tem três tipos de comentários: comentários tradicionais , comentários de fim de linha e comentários de documentação .
Os comentários tradicionais, também conhecidos como comentários em bloco, começam com /*
e terminam com */
, eles podem se estender por várias linhas. Esse tipo de comentário foi derivado de C e C ++.
/* This is a multi-line comment.
It may occupy more than one line. */
Os comentários de final de linha começam //
e se estendem até o final da linha atual. Este tipo de comentário também está presente em C ++ e em C.
// This is an end-of-line comment
Os comentários da documentação nos arquivos de origem são processados pela ferramenta Javadoc para gerar a documentação. Esse tipo de comentário é idêntico aos comentários tradicionais, exceto que começa /**
e segue as convenções definidas pela ferramenta Javadoc. Tecnicamente, esses comentários são um tipo especial de comentário tradicional e não são definidos especificamente na especificação da linguagem.
/**
* This is a documentation comment.
*
* @author John Doe
*/
Tipos universais
As classes no pacote java.lang são importadas implicitamente para todos os programas, desde que nenhum tipo importado explicitamente tenha os mesmos nomes. Os importantes incluem:
- java.lang.Object
- Tipo de topo do Java . Superclasse de todas as classes que não declaram uma classe pai. Todos os valores podem ser convertidos para esse tipo, embora para valores primitivos isso envolva autoboxing .
- java.lang.String
- Tipo de string básico do Java. Imutável . Alguns métodos tratam cada unidade de código UTF-16 como um "caractere", mas também estão disponíveis métodos para converter para um
int[]
que seja efetivamente UTF-32 . - java.lang.Throwable
- supertipo de tudo que pode ser lançado ou capturado com as instruções
throw
e do Javacatch
.
Estrutura do programa
Os aplicativos Java consistem em coleções de classes. As classes existem em pacotes, mas também podem ser aninhadas dentro de outras classes.
main
método
Todo aplicativo Java deve ter um ponto de entrada. Isso é verdadeiro tanto para aplicativos de interface gráfica quanto para aplicativos de console. O ponto de entrada é o main
método. Pode haver mais de uma classe com um main
método, mas a classe principal é sempre definida externamente (por exemplo, em um arquivo de manifesto ). O método deve ser static
e recebe argumentos de linha de comando como uma matriz de strings. Ao contrário do C ++ ou C # , ele nunca retorna um valor e deve retornar void
.
public static void main(String[] args) {
}
Pacotes
Os pacotes são parte do nome de uma classe e são usados para agrupar e / ou distinguir entidades nomeadas de outras. Outra finalidade dos pacotes é controlar o acesso ao código junto com os modificadores de acesso. Por exemplo, java.io.InputStream
é um nome de classe totalmente qualificado para a classe InputStream
que está localizada no pacote java.io
.
Um pacote é declarado no início do arquivo com a package
declaração:
package myapplication.mylibrary;
public class MyClass {
}
As classes com o public
modificador devem ser colocadas nos arquivos com o mesmo nome e extensão java e colocadas em pastas aninhadas correspondentes ao nome do pacote. A classe acima myapplication.mylibrary.MyClass
terá o seguinte caminho: myapplication/mylibrary/MyClass.java
.
Declaração de importação
Declaração de importação de tipo
Uma declaração de importação de tipo permite que um tipo nomeado seja referido por um nome simples em vez do nome completo que inclui o pacote. As declarações de importação podem ser declarações de importação de tipo único ou declarações de importação sob demanda . As declarações de importação devem ser colocadas na parte superior de um arquivo de código após a declaração do pacote.
package myPackage;
import java.util.Random; // Single type declaration
public class ImportsTest {
public static void main(String[] args) {
/* The following line is equivalent to
* java.util.Random random = new java.util.Random();
* It would've been incorrect without the import.
*/
Random random = new Random();
}
}
As declarações de importação sob demanda são mencionadas no código. Uma "importação de tipo" importa todos os tipos do pacote. Uma "importação estática" importa membros do pacote.
import java.util.*; /*This form of importing classes makes all classes
in package java.util available by name, could be used instead of the
import declaration in the previous example. */
import java.*; /*This statement is legal, but does nothing, since there
are no classes directly in package java. All of them are in packages
within package java. This does not import all available classes.*/
Declaração de importação estática
Este tipo de declaração está disponível desde J2SE 5.0 . As declarações de importação estáticas permitem o acesso a membros estáticos definidos em outra classe, interface, anotação ou enum; sem especificar o nome da classe:
import static java.lang.System.out; //'out' is a static field in java.lang.System
public class HelloWorld {
public static void main(String[] args) {
/* The following line is equivalent to:
System.out.println("Hi World!");
and would have been incorrect without the import declaration. */
out.println("Hello World!");
}
}
As declarações de importação sob demanda permitem importar todos os campos do tipo:
import static java.lang.System.*;
/* This form of declaration makes all
fields in the java.lang.System class available by name, and may be used instead
of the import declaration in the previous example. */
As constantes Enum também podem ser usadas com importação estática. Por exemplo, este enum está no pacote denominado screen
:
public enum ColorName {
RED, BLUE, GREEN
};
É possível usar declarações de importação estáticas em outra classe para recuperar as constantes enum:
import screen.ColorName;
import static screen.ColorName.*;
public class Dots {
/* The following line is equivalent to 'ColorName foo = ColorName.RED',
and it would have been incorrect without the static import. */
ColorName foo = RED;
void shift() {
/* The following line is equivalent to:
if (foo == ColorName.RED) foo = ColorName.BLUE; */
if (foo == RED) foo = BLUE;
}
}
Operadores
Os operadores em Java são semelhantes aos de C ++ . No entanto, não há nenhum delete
operador devido aos mecanismos de coleta de lixo em Java e não há operações em ponteiros, uma vez que o Java não os suporta. Outra diferença é que Java tem um operador de deslocamento à direita sem sinal ( >>>
), enquanto a sinalização do operador de deslocamento à direita de C depende do tipo. Os operadores em Java não podem ser sobrecarregados .
Precedência | Operador | Descrição | Associatividade |
---|---|---|---|
1 |
()
|
Invocação de método | Da esquerda para direita |
[]
|
Acesso de matriz | ||
.
|
Seleção de membros da classe | ||
2 |
++ --
|
Incremento e decremento Postfix | |
3 |
++ --
|
Aumento e diminuição do prefixo | Direita para esquerda |
+ -
|
Mais e menos unários | ||
! ~
|
NOT lógico e NOT bit a bit | ||
(type) val
|
Tipo elenco | ||
new
|
Instância de classe ou criação de array | ||
4 |
* / %
|
Multiplicação, divisão e módulo (resto) | Da esquerda para direita |
5 |
+ -
|
Adição e subtração | |
+
|
Concatenação de string | ||
6 |
<< >> >>>
|
Deslocamento para a esquerda bit a bit , deslocamento para a direita sinalizado e deslocamento para a direita não sinalizado | |
7 |
< <=
|
Relacional “menor que” e “menor ou igual a” | |
> >=
|
Relacional “maior que” e “maior ou igual a” | ||
instanceof
|
Comparação de tipo | ||
8 |
== !=
|
Relacional “igual a” e “diferente de” | |
9 |
&
|
E lógico e bit a bit | |
10 |
^
|
XOR bit a bit e lógico (exclusivo ou) | |
11 |
|
|
OR lógico e bit a bit (inclusivo ou) | |
12 |
&&
|
Condicional lógico-AND | |
13 |
||
|
OR condicional lógico | |
14 |
c ? t : f
|
Ternário condicional (ver :? ) | Direita para esquerda |
15 |
=
|
Tarefa simples | |
+= -=
|
Atribuição por soma e diferença | ||
*= /= %=
|
Atribuição por produto, quociente e resto | ||
<<= >>= >>>=
|
Atribuição por deslocamento à esquerda bit a bit, deslocamento à direita assinado e deslocamento à direita não assinado | ||
&= ^= |=
|
Atribuição por bit a bit AND, XOR e OR |
Estruturas de controle
Declarações condicionais
if
demonstração
As instruções if em Java são semelhantes às de C e usam a mesma sintaxe:
if (i == 3) doSomething();
if
declaração pode incluir else
bloco opcional , caso em que se torna uma declaração if-then-else:
if (i == 2) {
doSomething();
} else {
doSomethingElse();
}
Como C, a construção else-if não envolve nenhuma palavra-chave especial, é formada como uma sequência de instruções if-then-else separadas:
if (i == 3) {
doSomething();
} else if (i == 2) {
doSomethingElse();
} else {
doSomethingDifferent();
}
Além disso, observe que o operador ?: Pode ser usado no lugar da instrução if simples, por exemplo
int a = 1;
int b = 2;
int minVal = (a < b) ? a : b;
switch
demonstração
Interruptor declarações em Java pode usar byte
, short
, char
, e int
(nota: não long
) tipos de dados primitivos ou seus tipos de mensagens publicitárias correspondentes. A partir do J2SE 5.0, é possível usar tipos enum . A partir do Java SE 7, é possível usar Strings. Outros tipos de referência não podem ser usados em switch
instruções.
Os valores possíveis são listados usando case
rótulos. Esses rótulos em Java podem conter apenas constantes (incluindo constantes enum e constantes de string). A execução começará após o rótulo correspondente à expressão entre colchetes. Um default
rótulo opcional pode estar presente para declarar que o código seguinte será executado se nenhum dos rótulos de caso corresponder à expressão.
O código de cada etiqueta termina com a break
palavra - chave. É possível omiti-lo fazendo com que a execução prossiga para o próximo rótulo, no entanto, um aviso normalmente será relatado durante a compilação.
switch (ch) {
case 'A':
doSomething(); // Triggered if ch == 'A'
break;
case 'B':
case 'C':
doSomethingElse(); // Triggered if ch == 'B' or ch == 'C'
break;
default:
doSomethingDifferent(); // Triggered in any other case
break;
}
switch
expressões
Desde o Java 14, tornou-se possível usar expressões switch, que usam a nova sintaxe de seta:
var result = switch (ch) {
case 'A' -> Result.GREAT;
case 'B', 'C' -> Result.FINE;
default -> throw new ThisIsNoGoodException();
};
Alternativamente, existe a possibilidade de expressar o mesmo com a yield
afirmação, embora seja recomendado preferir a sintaxe da seta, pois evita o problema de quedas acidentais.
var result = switch (ch) {
case 'A':
yield Result.GREAT;
case 'B':
case 'C':
yield Result.FINE;
default:
throw new ThisIsNoGoodException();
};
Declarações de iteração
As instruções de iteração são instruções executadas repetidamente quando uma determinada condição é avaliada como verdadeira. Desde J2SE 5.0 , Java tem quatro formas de tais instruções.
while
ciclo
No while
loop, o teste é feito antes de cada iteração.
while (i < 10) {
doSomething();
}
do ... while
ciclo
No do ... while
loop, o teste é feito após cada iteração. Conseqüentemente, o código é sempre executado pelo menos uma vez.
// doSomething() is called at least once
do {
doSomething();
} while (i < 10);
for
ciclo
for
loops em Java incluem um inicializador, uma condição e uma expressão de contador. É possível incluir várias expressões do mesmo tipo usando vírgula como delimitador (exceto na condição). No entanto, ao contrário de C, a vírgula é apenas um delimitador e não um operador.
for (int i = 0; i < 10; i++) {
doSomething();
}
// A more complex loop using two variables
for (int i = 0, j = 9; i < 10; i++, j -= 3) {
doSomething();
}
Como C, todas as três expressões são opcionais. O seguinte loop é infinito:
for (;;) {
doSomething();
}
for
Loop aprimorado
Os for
loops aprimorados estão disponíveis desde o J2SE 5.0 . Este tipo de loop usa iteradores embutidos em arrays e coleções para retornar cada item na coleção fornecida. Cada elemento é retornado e acessível no contexto do bloco de código. Quando o bloco é executado, o próximo item é devolvido até que não haja itens restantes. Ao contrário do C # , esse tipo de loop não envolve uma palavra-chave especial, mas, em vez disso, usa um estilo de notação diferente.
for (int i : intArray) {
doSomething(i);
}
Declarações de salto
Etiquetas
Os rótulos recebem pontos no código usado por instruções break
e continue
. Observe que a goto
palavra-chave Java não pode ser usada para pular para pontos específicos no código.
start:
someMethod();
break
demonstração
A break
declaração sai do loop ou switch
declaração mais próxima . A execução continua na instrução após a instrução encerrada, se houver.
for (int i = 0; i < 10; i++) {
while (true) {
break;
}
// Will break to this point
}
É possível quebrar o loop externo usando rótulos:
outer:
for (int i = 0; i < 10; i++) {
while (true) {
break outer;
}
}
// Will break to this point
continue
demonstração
A continue
instrução interrompe a iteração atual da instrução de controle atual e começa a próxima iteração. O seguinte while
loop no código abaixo lê caracteres chamando getChar()
, ignorando as instruções no corpo do loop se os caracteres forem espaços:
int ch;
while (ch == getChar()) {
if (ch == ' ') {
continue; // Skips the rest of the while-loop
}
// Rest of the while-loop, will not be reached if ch == ' '
doSomething();
}
Os rótulos podem ser especificados em continue
declarações e break
declarações:
outer:
for (String str : stringsArr) {
char[] strChars = str.toCharArray();
for (char ch : strChars) {
if (ch == ' ') {
/* Continues the outer cycle and the next
string is retrieved from stringsArr */
continue outer;
}
doSomething(ch);
}
}
return
demonstração
A return
instrução é usada para encerrar a execução do método e retornar um valor. Um valor retornado pelo método é escrito após a return
palavra - chave. Se o método retornar qualquer coisa menos void
, ele deve usar a return
instrução para retornar algum valor.
void doSomething(boolean streamClosed) {
// If streamClosed is true, execution is stopped
if (streamClosed) {
return;
}
readFromStream();
}
int calculateSum(int a, int b) {
int result = a + b;
return result;
}
return
instrução termina a execução imediatamente, exceto em um caso: se a instrução for encontrada dentro de um try
bloco e for complementada por a finally
, o controle é passado para o finally
bloco.
void doSomething(boolean streamClosed) {
try {
if (streamClosed) {
return;
}
readFromStream();
} finally {
/* Will be called last even if
readFromStream() was not called */
freeResources();
}
}
Declarações de tratamento de exceções
try-catch-finally
afirmações
As exceções são gerenciadas em try
... catch
blocos.
try {
// Statements that may throw exceptions
methodThrowingExceptions();
} catch (Exception ex) {
// Exception caught and handled here
reportException(ex);
} finally {
// Statements always executed after the try/catch blocks
freeResources();
}
As instruções dentro do try
bloco são executadas e, se alguma delas lançar uma exceção, a execução do bloco é interrompida e a exceção é tratada pelo catch
bloco. Pode haver vários catch
blocos, caso em que o primeiro bloco com uma variável de exceção cujo tipo corresponde ao tipo da exceção lançada é executado.
Java SE 7 também introduziu cláusulas multi-catch além de cláusulas uni-catch. Esse tipo de cláusula catch permite que Java trate diferentes tipos de exceções em um único bloco, desde que não sejam subclasses umas das outras.
try {
methodThrowingExceptions();
} catch (IOException | IllegalArgumentException ex) {
//Both IOException and IllegalArgumentException will be caught and handled here
reportException(ex);
}
Se nenhum catch
bloco corresponder ao tipo da exceção lançada, a execução do bloco externo (ou método) que contém a instrução try
... catch
é descontinuada e a exceção é passada para fora do bloco (ou método) que o contém. A exceção é propagada para cima através da pilha de chamadas até que um catch
bloco correspondente seja encontrado em um dos métodos ativos no momento. Se a exceção se propagar até o main
método mais superior sem que um catch
bloco correspondente seja encontrado, uma descrição textual da exceção é gravada no fluxo de saída padrão.
As instruções dentro do finally
bloco são sempre executadas após os blocos try
e catch
, independentemente de uma exceção ter sido lançada ou não e mesmo que uma return
instrução tenha sido alcançada. Esses blocos são úteis para fornecer código de limpeza que é garantido que sempre será executado.
Os blocos catch
e finally
são opcionais, mas pelo menos um ou outro deve estar presente após o try
bloco.
try
-with-resources demonstrativos
try
As instruções -with-resources são um tipo especial de try-catch-finally
instruções introduzidas como uma implementação do padrão dispose em Java SE 7. Em uma try
instrução -with-resources a try
palavra-chave é seguida pela inicialização de um ou mais recursos que são liberados automaticamente quando a try
execução do bloco está terminado. Os recursos devem ser implementados java.lang.AutoCloseable
. try
As instruções -with-resources não precisam ter um bloco catch
ou finally
ao contrário das try-catch-finally
instruções normais .
try (FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos)) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
Desde o Java 9 é possível usar variáveis já declaradas:
FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos);
try (fos; xEnc) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
throw
demonstração
A throw
instrução é usada para lançar uma exceção e terminar a execução do bloco ou método. A instância de exceção lançada é escrita após a throw
instrução.
void methodThrowingExceptions(Object obj) {
if (obj == null) {
// Throws exception of NullPointerException type
throw new NullPointerException();
}
// Will not be called, if object is null
doSomethingWithObject(obj);
}
Controle de simultaneidade de thread
Java possui ferramentas integradas para programação multi-thread . Para fins de sincronização de thread, a synchronized
instrução está incluída na linguagem Java.
Para sincronizar um bloco de código, ele é precedido pela synchronized
palavra - chave seguida pelo objeto de bloqueio dentro dos colchetes. Quando o thread em execução atinge o bloco sincronizado, ele adquire um bloqueio de exclusão mútuo , executa o bloco e, em seguida, libera o bloqueio. Nenhum thread pode entrar neste bloco até que o bloqueio seja liberado. Qualquer tipo de referência não nulo pode ser usado como bloqueio.
/* Acquires lock on someObject. It must be of
a reference type and must be non-null */
synchronized (someObject) {
// Synchronized statements
}
assert
demonstração
assert
declarações estão disponíveis desde J2SE 1.4 . Esses tipos de instruções são usados para fazer afirmações no código-fonte, que podem ser ativadas e desativadas durante a execução de classes ou pacotes específicos. Para declarar uma asserção, a assert
palavra-chave é usada seguida por uma expressão condicional. Se for avaliado false
quando a instrução é executada, uma exceção é lançada. Essa instrução pode incluir dois pontos seguidos por outra expressão, que atuará como a mensagem de detalhe da exceção.
// If n equals 0, AssertionError is thrown
assert n != 0;
/* If n equals 0, AssertionError will be thrown
with the message after the colon */
assert n != 0 : "n was equal to zero";
Tipos primitivos
Os tipos primitivos em Java incluem tipos inteiros, números de ponto flutuante, unidades de código UTF-16 e um tipo booleano. Não há tipos não assinados em Java, exceto o char
tipo, que é usado para representar unidades de código UTF-16. A falta de tipos sem sinal é compensada pela introdução da operação de deslocamento para a direita sem sinal ( >>>
), que não está presente em C ++. No entanto, foram levantadas críticas sobre a falta de compatibilidade com C e C ++ que isso causa.
Tipos primitivos | |||||
---|---|---|---|---|---|
Digite o nome | Classe Wrapper | Valor | Faixa | Tamanho | Valor padrão |
byte
|
java.lang.Byte
|
inteiro | -128 a +127 | 8 bits (1 byte) |
0
|
short
|
java.lang.Short
|
inteiro | -32.768 a +32.767 | 16 bits (2 bytes) |
0
|
int
|
java.lang.Integer
|
inteiro | -2.147.483.648 a +2.147.483.647 | 32 bits (4 bytes) |
0
|
long
|
java.lang.Long
|
inteiro | -9.223.372.036.854.775.808 a +9.223.372.036.854.775.807 |
64 bits (8 bytes) |
0
|
float
|
java.lang.Float
|
número de ponto flutuante | ± 1,401298E − 45 a ± 3,402823E + 38 | 32 bits (4 bytes) |
0.0f
|
double
|
java.lang.Double
|
número de ponto flutuante | ± 4,94065645841246E-324 a ± 1,79769313486232E + 308 |
64 bits (8 bytes) |
0.0
|
boolean
|
java.lang.Boolean
|
boleano |
true ou false
|
1 bit (1 bit) |
false
|
char
|
java.lang.Character
|
Unidade de código UTF-16 ( caractere BMP ou parte de um par substituto) |
'\u0000' Através dos '\uFFFF'
|
16 bits (2 bytes) |
'\u0000'
|
char
não corresponde necessariamente a um único caractere. Pode representar uma parte de um par substituto , caso em que o ponto de código Unicode é representado por uma sequência de dois char
valores.
Boxe e unboxing
Este recurso de linguagem foi introduzido no J2SE 5.0 . Boxing é a operação de conversão de um valor de um tipo primitivo em um valor de um tipo de referência correspondente, que serve como um invólucro para esse tipo primitivo específico. Unboxing é a operação reversa de converter um valor de um tipo de referência (previamente encaixotado) em um valor de um tipo primitivo correspondente. Nenhuma das operações requer uma conversão explícita.
Exemplo:
int foo = 42; // Primitive type
Integer bar = foo; /* foo is boxed to bar, bar is of Integer type,
which serves as a wrapper for int */
int foo2 = bar; // Unboxed back to primitive type
Tipos de referência
Os tipos de referência incluem tipos de classe, tipos de interface e tipos de array. Quando o construtor é chamado, um objeto é criado no heap e uma referência é atribuída à variável. Quando uma variável de um objeto sai do escopo, a referência é quebrada e quando não há referências restantes, o objeto é marcado como lixo. O coletor de lixo então coleta e destrói algum tempo depois.
Uma variável de referência é null
quando ela não faz referência a nenhum objeto.
Arrays
Arrays em Java são criados em tempo de execução, assim como as instâncias de classe. O comprimento da matriz é definido na criação e não pode ser alterado.
int[] numbers = new int[5];
numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
Inicializadores
// Long syntax
int[] numbers = new int[] {20, 1, 42, 15, 34};
// Short syntax
int[] numbers2 = {20, 1, 42, 15, 34};
Matrizes multidimensionais
Em Java, arrays multidimensionais são representados como arrays de arrays. Tecnicamente, eles são representados por matrizes de referências a outras matrizes.
int[][] numbers = new int[3][3];
numbers[1][2] = 2;
int[][] numbers2 = {{2, 3, 2}, {1, 2, 6}, {2, 4, 5}};
Devido à natureza das matrizes multidimensionais, as submatrizes podem variar em comprimento, portanto, as matrizes multidimensionais não são obrigadas a ser retangulares, ao contrário de C:
int[][] numbers = new int[2][]; //Initialization of the first dimension only
numbers[0] = new int[3];
numbers[1] = new int[2];
Aulas
As classes são fundamentos de uma linguagem orientada a objetos, como Java. Eles contêm membros que armazenam e manipulam dados. As classes são divididas em nível superior e aninhadas . Classes aninhadas são classes colocadas dentro de outra classe que pode acessar os membros privados da classe envolvente. As classes aninhadas incluem classes de membro (que podem ser definidas com o modificador estático para aninhamento simples ou sem ele para classes internas), classes locais e classes anônimas .
Declaração
Classe de nível superior |
class Foo {
// Class members
}
|
---|---|
Classe interna |
class Foo { // Top-level class
class Bar { // Inner class
}
}
|
Classe aninhada |
class Foo { // Top-level class
static class Bar { // Nested class
}
}
|
Aula local |
class Foo {
void bar() {
class Foobar {// Local class within a method
}
}
}
|
Aula anônima |
class Foo {
void bar() {
new Object() {// Creation of a new anonymous class extending Object
};
}
}
|
Instanciação
Membros não estáticos de uma classe definem os tipos de variáveis e métodos de instância , que estão relacionados aos objetos criados a partir dessa classe. Para criar esses objetos, a classe deve ser instanciada usando o new
operador e chamando o construtor da classe.
Foo foo = new Foo();
Acessando membros
Membros de ambas as instâncias e classes estáticas são acessados com o .
operador (ponto).
Acessando um membro da
instância Os membros da instância podem ser acessados por meio do nome de uma variável.
String foo = "Hello";
String bar = foo.toUpperCase();
Acessando um membro da classe
estática Os membros estáticos são acessados usando o nome da classe ou qualquer outro tipo. Isso não requer a criação de uma instância de classe. Membros estáticos são declarados usando o static
modificador.
public class Foo {
public static void doSomething() {
}
}
// Calling the static method
Foo.doSomething();
Modificadores
Modificadores são palavras-chave usadas para modificar declarações de tipos e membros de tipo. Mais notavelmente, há um subgrupo que contém os modificadores de acesso.
-
abstract
- especifica que uma classe serve apenas como uma classe base e não pode ser instanciada. -
static
- Usado apenas para classes de membros, especifica que a classe de membros não pertence a uma instância específica da classe que os contém. -
final
- As classes marcadas comofinal
não podem ser estendidas e não podem ter nenhuma subclasse. -
strictfp
- Especifica que todas as operações de ponto flutuante devem ser realizadas em conformidade com IEEE 754 e proíbe o uso de precisão aprimorada para armazenar resultados intermediários.
Modificadores de acesso
Os modificadores de acesso , ou modificadores de herança , definem a acessibilidade de classes, métodos e outros membros. Membros marcados como public
podem ser contatados de qualquer lugar. Se uma classe ou seu membro não tiver nenhum modificador, o acesso padrão será assumido.
public class Foo {
int go() {
return 0;
}
private class Bar {
}
}
A tabela a seguir mostra se o código dentro de uma classe tem acesso à classe ou método, dependendo do local da classe de acesso e do modificador para a classe acessada ou membro da classe:
Modificador | Mesma classe ou classe aninhada | Outra classe dentro do mesmo pacote | Aula estendida dentro de outro pacote | Não estendido dentro de outro pacote |
---|---|---|---|---|
private
|
sim | não | não | não |
padrão (pacote privado) | sim | sim | não | não |
protected
|
sim | sim | sim | não |
public
|
sim | sim | sim | sim |
Construtores e inicializadores
Um construtor é um método especial chamado quando um objeto é inicializado. Seu objetivo é inicializar os membros do objeto. As principais diferenças entre construtores e métodos comuns são que os construtores são chamados apenas quando uma instância da classe é criada e nunca retornam nada. Os construtores são declarados como métodos comuns, mas são nomeados de acordo com a classe e nenhum tipo de retorno é especificado:
class Foo {
String str;
Foo() { // Constructor with no arguments
// Initialization
}
Foo(String str) { // Constructor with one argument
this.str = str;
}
}
Inicializadores são blocos de código executados quando uma classe ou instância de uma classe é criada. Existem dois tipos de inicializadores, inicializadores estáticos e inicializadores de instância .
Os inicializadores estáticos inicializam campos estáticos quando a classe é criada. Eles são declarados usando a static
palavra-chave:
class Foo {
static {
// Initialization
}
}
Uma classe é criada apenas uma vez. Portanto, os inicializadores estáticos não são chamados mais de uma vez. Ao contrário, os inicializadores de instância são chamados automaticamente antes da chamada a um construtor sempre que uma instância da classe é criada. Ao contrário dos construtores, os inicializadores de instância não podem aceitar nenhum argumento e geralmente não podem lançar nenhuma exceção verificada (exceto em vários casos especiais). Os inicializadores de instância são declarados em um bloco sem nenhuma palavra-chave:
class Foo {
{
// Initialization
}
}
Como o Java tem um mecanismo de coleta de lixo, não há destruidores . No entanto, cada objeto tem um finalize()
método chamado antes da coleta de lixo, que pode ser substituído para implementar a finalização.
Métodos
Todas as instruções em Java devem residir em métodos. Os métodos são semelhantes às funções, exceto que pertencem a classes. Um método tem um valor de retorno, um nome e geralmente alguns parâmetros inicializados quando é chamado com alguns argumentos. Semelhante ao C ++, os métodos que não retornam nada têm o tipo de retorno declarado como void
. Ao contrário do C ++, os métodos em Java não podem ter valores de argumento padrão e os métodos são geralmente sobrecarregados.
class Foo {
int bar(int a, int b) {
return (a*2) + b;
}
/* Overloaded method with the same name but different set of arguments */
int bar(int a) {
return a*2;
}
}
Um método é chamado usando .
notação em um objeto, ou no caso de um método estático, também no nome de uma classe.
Foo foo = new Foo();
int result = foo.bar(7, 2); // Non-static method is called on foo
int finalResult = Math.abs(result); // Static method call
A throws
palavra-chave indica que um método lança uma exceção. Todas as exceções verificadas devem ser listadas em uma lista separada por vírgulas.
void openStream() throws IOException, myException { // Indicates that IOException may be thrown
}
Modificadores
-
abstract
- Métodos abstratos podem estar presentes apenas em classes abstratas , tais métodos não têm corpo e devem ser sobrescritos em uma subclasse, a menos que seja ele próprio abstrato. -
static
- Torna o método estático e acessível sem a criação de uma instância de classe. No entanto, os métodos estáticos não podem acessar membros não estáticos na mesma classe. -
final
- Declara que o método não pode ser sobrescrito em uma subclasse. -
native
- Indica que este método é implementado por meio de JNI no código dependente da plataforma. A implementação real acontece fora do código Java e esses métodos não têm corpo. -
strictfp
- Declara conformidade estrita com IEEE 754 na realização de operações de ponto flutuante. -
synchronized
- Declara que uma thread executando este método deve adquirir o monitor. Parasynchronized
métodos, o monitor é a instância da classe oujava.lang.Class
se o método é estático. - Modificadores de acesso - idênticos aos usados com as classes.
Varargs
Este recurso de linguagem foi introduzido no J2SE 5.0 . O último argumento do método pode ser declarado como um parâmetro de aridade variável, caso em que o método se torna um método de aridade variável (em oposição aos métodos de aridade fixa) ou simplesmente método varargs . Isso permite que se passe um número variável de valores, do tipo declarado, para o método como parâmetros - incluindo nenhum parâmetro. Esses valores estarão disponíveis dentro do método como um array.
void printReport(String header, int... numbers) { //numbers represents varargs
System.out.println(header);
for (int num : numbers) {
System.out.println(num);
}
}
// Calling varargs method
printReport("Report data", 74, 83, 25, 96);
Campos
Campos, ou variáveis de classe , podem ser declarados dentro do corpo da classe para armazenar dados.
class Foo {
double bar;
}
Os campos podem ser inicializados diretamente quando declarados.
class Foo {
double bar = 2.3;
}
Modificadores
-
static
- Torna o campo um membro estático. -
final
- Permite que o campo seja inicializado apenas uma vez em um construtor ou dentro do bloco de inicialização ou durante sua declaração, o que ocorrer primeiro. -
transient
- Indica que este campo não será armazenado durante a serialização . -
volatile
- Se um campo for declaradovolatile
, é garantido que todos os threads vejam um valor consistente para a variável.
Herança
As classes em Java só podem herdar de uma classe. Uma classe pode ser derivada de qualquer classe que não esteja marcada como final
. A herança é declarada usando a extends
palavra - chave. Uma classe pode fazer referência a si mesma usando a this
palavra - chave e sua superclasse direta usando a super
palavra - chave.
class Foo {
}
class Foobar extends Foo {
}
Se uma classe não especifica sua superclasse, ela herda implicitamente da java.lang.Object
classe. Portanto, todas as classes em Java são subclasses de Object
classe.
Se a superclasse não tiver um construtor sem parâmetros, a subclasse deve especificar em seus construtores qual construtor da superclasse usar. Por exemplo:
class Foo {
public Foo(int n) {
// Do something with n
}
}
class Foobar extends Foo {
private int number;
// Superclass does not have constructor without parameters
// so we have to specify what constructor of our superclass to use and how
public Foobar(int number) {
super(number);
this.number = number;
}
}
Métodos de substituição
Ao contrário do C ++, todos os não final
métodos em Java são virtuais e podem ser substituídos pelas classes herdadas.
class Operation {
public int doSomething() {
return 0;
}
}
class NewOperation extends Operation {
@Override
public int doSomething() {
return 1;
}
}
Classes abstratas
Uma classe abstrata é uma classe que está incompleta ou deve ser considerada incompleta. As classes normais podem ter métodos abstratos, ou seja, métodos que são declarados, mas ainda não implementados, apenas se forem classes abstratas. Uma classe C tem métodos abstratos se qualquer uma das seguintes condições for verdadeira:
- C contém explicitamente uma declaração de um método abstrato.
- Qualquer uma das superclasses de C tem um método abstrato e C não declara nem herda um método que o implementa.
- Uma superinterface direta de C declara ou herda um método (que, portanto, é necessariamente abstrato) e C não declara nem herda um método que o implementa.
- Uma subclasse de uma classe abstrata que não é abstrata pode ser instanciada, resultando na execução de um construtor para a classe abstrata e, portanto, na execução dos inicializadores de campo para variáveis de instância dessa classe.
package org.dwwwp.test;
/**
* @author jcrypto
*/
public class AbstractClass {
private static final String hello;
static {
System.out.println(AbstractClass.class.getName() + ": static block runtime");
hello = "hello from " + AbstractClass.class.getName();
}
{
System.out.println(AbstractClass.class.getName() + ": instance block runtime");
}
public AbstractClass() {
System.out.println(AbstractClass.class.getName() + ": constructor runtime");
}
public static void hello() {
System.out.println(hello);
}
}
package org.dwwwp.test;
/**
* @author jcrypto
*/
public class CustomClass extends AbstractClass {
static {
System.out.println(CustomClass.class.getName() + ": static block runtime");
}
{
System.out.println(CustomClass.class.getName() + ": instance block runtime");
}
public CustomClass() {
System.out.println(CustomClass.class.getName() + ": constructor runtime");
}
public static void main(String[] args) {
CustomClass nc = new CustomClass();
hello();
//AbstractClass.hello();//also valid
}
}
Saída:
org.dwwwp.test.AbstractClass: static block runtime
org.dwwwp.test.CustomClass: static block runtime
org.dwwwp.test.AbstractClass: instance block runtime
org.dwwwp.test.AbstractClass: constructor runtime
org.dwwwp.test.CustomClass: instance block runtime
org.dwwwp.test.CustomClass: constructor runtime
hello from org.dwwwp.test.AbstractClass
Enumerações
Este recurso de linguagem foi introduzido no J2SE 5.0 . Tecnicamente, as enumerações são um tipo de classe que contém constantes de enum em seu corpo. Cada constante de enum define uma instância do tipo de enum. As classes de enumeração não podem ser instanciadas em qualquer lugar, exceto na própria classe de enumeração.
enum Season {
WINTER, SPRING, SUMMER, AUTUMN
}
As constantes de Enum podem ter construtores, que são chamados quando a classe é carregada:
public enum Season {
WINTER("Cold"), SPRING("Warmer"), SUMMER("Hot"), AUTUMN("Cooler");
Season(String description) {
this.description = description;
}
private final String description;
public String getDescription() {
return description;
}
}
Enumerações podem ter corpos de classe, caso em que são tratadas como classes anônimas que estendem a classe enum:
public enum Season {
WINTER {
String getDescription() {return "cold";}
},
SPRING {
String getDescription() {return "warmer";}
},
SUMMER {
String getDescription() {return "hot";}
},
FALL {
String getDescription() {return "cooler";}
};
}
Interfaces
As interfaces são tipos que não contêm campos e geralmente definem vários métodos sem uma implementação real. Eles são úteis para definir um contrato com qualquer número de implementações diferentes. Cada interface é implicitamente abstrata. Os métodos de interface podem ter um subconjunto de modificadores de acesso dependendo da versão da linguagem,, strictfp
que tem o mesmo efeito que para as classes, e também static
desde Java SE 8.
interface ActionListener {
int ACTION_ADD = 0;
int ACTION_REMOVE = 1;
void actionSelected(int action);
}
Implementando uma interface
Uma interface é implementada por uma classe usando a implements
palavra - chave. É permitido implementar mais de uma interface, caso em que são escritas após a implements
palavra-chave em uma lista separada por vírgulas. A classe que implementa uma interface deve sobrescrever todos os seus métodos, caso contrário, deve ser declarada como abstrata.
interface RequestListener {
int requestReceived();
}
class ActionHandler implements ActionListener, RequestListener {
public void actionSelected(int action) {
}
public int requestReceived() {
}
}
//Calling method defined by interface
RequestListener listener = new ActionHandler(); /*ActionHandler can be
represented as RequestListener...*/
listener.requestReceived(); /*...and thus is known to implement
requestReceived() method*/
Interfaces funcionais e expressões lambda
Esses recursos foram introduzidos com o lançamento do Java SE 8. Uma interface se torna automaticamente uma interface funcional se definir apenas um método. Nesse caso, uma implementação pode ser representada como uma expressão lambda em vez de implementá-la em uma nova classe, simplificando muito a escrita de código no estilo funcional . As interfaces funcionais podem, opcionalmente, ser anotadas com a @FunctionalInterface
anotação, o que dirá ao compilador para verificar se a interface realmente está em conformidade com a definição de uma interface funcional.
// A functional interface
@FunctionalInterface
interface Calculation {
int calculate(int someNumber, int someOtherNumber);
}
// A method which accepts this interface as a parameter
int runCalculation(Calculation calculation) {
return calculation.calculate(1, 2);
}
// Using a lambda to call the method
runCalculation((number, otherNumber) -> number + otherNumber);
// Equivalent code which uses an anonymous class instead
runCalculation(new Calculation() {
@Override
public int calculate(int someNumber, int someOtherNumber) {
return someNumber + someOtherNumber;
}
})
Os tipos de parâmetros do Lambda não precisam ser totalmente especificados e podem ser inferidos da interface que implementa. O corpo do lambda pode ser escrito sem um bloco de corpo e uma return
instrução se for apenas uma expressão. Além disso, para aquelas interfaces que têm apenas um único parâmetro no método, os colchetes podem ser omitidos.
// Same call as above, but with fully specified types and a body block
runCalculation((int number, int otherNumber) -> {
return number + otherNumber;
});
// A functional interface with a method which has only a single parameter
interface StringExtender {
String extendString(String input);
}
// Initializing a variable of this type by using a lambda
StringExtender extender = input -> input + " Extended";
Referências de método
Não é necessário usar lambdas quando já existe um método nomeado compatível com a interface. Esse método pode ser passado em vez de um lambda usando uma referência de método. Existem vários tipos de referências de método:
Tipo de referência | Exemplo | Lambda equivalente |
---|---|---|
Estático | Integer::sum |
(number, otherNumber) -> number + otherNumber
|
Vinculado | "LongString"::substring |
index -> "LongString".substring(index)
|
Não consolidado | String::isEmpty |
string -> string.isEmpty()
|
Construtor de classe | ArrayList<String>::new |
capacity -> new ArrayList<String>(capacity)
|
Construtor de matriz | String[]::new |
size -> new String[size]
|
O código acima do qual as chamadas runCalculation
podem ser substituídas pelo seguinte usando as referências de método:
runCalculation(Integer::sum);
Herança
As interfaces podem herdar de outras interfaces, assim como as classes. Ao contrário das classes, é permitido herdar de várias interfaces. Porém, é possível que várias interfaces possuam um campo com o mesmo nome, caso em que se torna um único membro ambíguo, que não pode ser acessado.
/* Class implementing this interface must implement methods of both
ActionListener and RequestListener */
interface EventListener extends ActionListener, RequestListener {
}
Métodos padrão
Java SE 8 introduziu métodos padrão para interfaces que permitem aos desenvolvedores adicionar novos métodos para interfaces existentes sem quebrar a compatibilidade com as classes que já implementam a interface. Ao contrário dos métodos de interface regulares, os métodos padrão têm um corpo que será chamado caso a classe de implementação não o substitua.
interface StringManipulator {
String extendString(String input);
// A method which is optional to implement
default String shortenString(String input) {
return input.substring(1);
}
}
// This is a valid class despite not implementing all the methods
class PartialStringManipulator implements StringManipulator {
@Override
public String extendString(String input) {
return input + " Extended";
}
}
Métodos estáticos
Os métodos estáticos são outro recurso de linguagem introduzido no Java SE 8. Eles se comportam exatamente da mesma maneira que nas classes.
interface StringUtils {
static String shortenByOneSymbol(String input) {
return input.substring(1);
}
}
StringUtils.shortenByOneSymbol("Test");
Métodos privados
Métodos privados foram adicionados na versão Java 9. Uma interface pode ter um método com um corpo marcado como privado, caso em que não será visível para as classes herdadas. Ele pode ser chamado a partir de métodos padrão para fins de reutilização de código.
interface Logger {
default void logError() {
log(Level.ERROR);
}
default void logInfo() {
log(Level.INFO);
}
private void log(Level level) {
SystemLogger.log(level.id);
}
}
Anotações
As anotações em Java são uma forma de incorporar metadados ao código. Este recurso de linguagem foi introduzido no J2SE 5.0 .
Tipos de anotação
Java possui um conjunto de tipos de anotação predefinidos, mas é permitido definir novos. Uma declaração de tipo de anotação é um tipo especial de uma declaração de interface. Eles são declarados da mesma forma que as interfaces, exceto que a interface
palavra-chave é precedida pelo @
sinal. Todas as anotações são implicitamente estendidas java.lang.annotation.Annotation
e não podem ser estendidas de mais nada.
@interface BlockingOperations {
}
As anotações podem ter as mesmas declarações no corpo que as interfaces comuns, além de poderem incluir enums e anotações. A principal diferença é que as declarações de métodos abstratos não devem ter parâmetros ou lançar exceções. Além disso, eles podem ter um valor padrão, que é declarado usando a default
palavra - chave após o nome do método:
@interface BlockingOperations {
boolean fileSystemOperations();
boolean networkOperations() default false;
}
Uso de anotações
As anotações podem ser usadas em qualquer tipo de declaração, seja pacote, classe (incluindo enums), interface (incluindo anotações), campo, método, parâmetro, construtor ou variável local. Também podem ser usados com constantes enum. As anotações são declaradas usando o @
sinal que precede o nome do tipo de anotação, após o qual os pares de elemento-valor são escritos entre colchetes. Todos os elementos sem valor padrão devem receber um valor.
@BlockingOperations(/*mandatory*/ fileSystemOperations,
/*optional*/ networkOperations = true)
void openOutputStream() { //Annotated method
}
Além da forma genérica, existem duas outras formas de declaração de anotação, que são abreviações. A anotação do marcador é uma forma abreviada, usada quando nenhum valor é atribuído aos elementos:
@Unused // Shorthand for @Unused()
void travelToJupiter() {
}
A outra forma abreviada é chamada de anotação de elemento único . É usado com tipos de anotações contendo apenas um elemento ou no caso em que vários elementos estão presentes, mas apenas um elemento não possui um valor padrão. No formulário de anotação de elemento único, o nome do elemento é omitido e apenas o valor é escrito em seu lugar:
/* Equivalent for @BlockingOperations(fileSystemOperations = true).
networkOperations has a default value and
does not have to be assigned a value */
@BlockingOperations(true)
void openOutputStream() {
}
Genéricos
Genéricos , ou tipos parametrizados, ou polimorfismo paramétrico é um dos principais recursos introduzidos no J2SE 5.0 . Antes da introdução dos genéricos, era necessário declarar todos os tipos explicitamente. Com os genéricos, tornou-se possível trabalhar de maneira semelhante com diferentes tipos sem declarar os tipos exatos. O principal objetivo dos genéricos é garantir a segurança de tipo e detectar erros de tempo de execução durante a compilação. Ao contrário do C #, as informações sobre os parâmetros usados não estão disponíveis no tempo de execução devido ao apagamento do tipo .
Classes genéricas
As classes podem ser parametrizadas adicionando uma variável de tipo dentro dos colchetes angulares ( <
e >
) após o nome da classe. Torna possível o uso desta variável de tipo em membros de classe em vez de tipos reais. Pode haver mais de uma variável de tipo, caso em que são declaradas em uma lista separada por vírgulas.
É possível limitar uma variável de tipo a um subtipo de alguma classe específica ou declarar uma interface que deve ser implementada pelo tipo. Nesse caso, a variável de tipo é anexada pela extends
palavra - chave seguida por um nome da classe ou da interface. Se a variável for restringida pela classe e pela interface ou se houver várias interfaces, o nome da classe será escrito primeiro, seguido pelos nomes das interfaces com o &
sinal usado como delimitador.
/* This class has two type variables, T and V. T must be
a subtype of ArrayList and implement Formattable interface */
public class Mapper<T extends ArrayList & Formattable, V> {
public void add(T array, V item) {
// array has add method because it is an ArrayList subclass
array.add(item);
}
}
Quando uma variável de um tipo parametrizado é declarada ou uma instância é criada, seu tipo é escrito exatamente no mesmo formato do cabeçalho da classe, exceto que o tipo real é escrito no lugar da declaração da variável de tipo.
/* Mapper is created with CustomList as T and Integer as V.
CustomList must be a subclass of ArrayList and implement Formattable */
Mapper<CustomList, Integer> mapper = new Mapper<CustomList, Integer>();
Desde o Java SE 7, é possível usar um diamante ( <>
) no lugar dos argumentos de tipo, caso em que o último será inferido. O código a seguir em Java SE 7 é equivalente ao código do exemplo anterior:
Mapper<CustomList, Integer> mapper = new Mapper<>();
Ao declarar uma variável para um tipo parametrizado, é possível usar curingas em vez de nomes de tipo explícitos. Os curingas são expressos escrevendo o ?
sinal em vez do tipo real. É possível limitar os tipos possíveis às subclasses ou superclasses de alguma classe específica, escrevendo a extends
palavra - chave ou a super
palavra - chave seguida de forma correspondente pelo nome da classe.
/* Any Mapper instance with CustomList as the first parameter
may be used regardless of the second one.*/
Mapper<CustomList, ?> mapper;
mapper = new Mapper<CustomList, Boolean>();
mapper = new Mapper<CustomList, Integer>();
/* Will not accept types that use anything but
a subclass of Number as the second parameter */
void addMapper(Mapper<?, ? extends Number> mapper) {
}
Métodos e construtores genéricos
O uso de genéricos pode ser limitado a alguns métodos específicos; esse conceito também se aplica a construtores. Para declarar um método parametrizado, as variáveis de tipo são escritas antes do tipo de retorno do método no mesmo formato das classes genéricas. No caso do construtor, as variáveis de tipo são declaradas antes do nome do construtor.
class Mapper {
// The class itself is not generic, the constructor is
<T, V> Mapper(T array, V item) {
}
}
/* This method will accept only arrays of the same type as
the searched item type or its subtype*/
static <T, V extends T> boolean contains(T item, V[] arr) {
for (T currentItem : arr) {
if (item.equals(currentItem)) {
return true;
}
}
return false;
}
Interfaces genéricas
As interfaces podem ser parametrizadas de maneira semelhante às classes.
interface Expandable<T extends Number> {
void addItem(T item);
}
// This class is parameterized
class Array<T extends Number> implements Expandable<T> {
void addItem(T item) {
}
}
// And this is not and uses an explicit type instead
class IntegerArray implements Expandable<Integer> {
void addItem(Integer item) {
}
}
Veja também
Referências
- Patrick Naughton , Herbert Schildt . Java 2: The Complete Reference , terceira edição. The McGraw-Hill Companies, 1999. ISBN 0-07-211976-4
- Vermeulen; Ambler; Bumgardner; Metz; Misfeldt; Shur; Thompson (2000). Os elementos do estilo Java . Cambridge University Press. ISBN 0-521-77768-2.
- Gosling, James ; Joy, Bill ; Steele, Guy ; Bracha, Gillad (2005). Especificação da linguagem Java (3ª ed.). Addison-Wesley Professional . Página visitada em 2008-12-03 .
links externos
- The Java Language Specification, terceira edição Descrição oficial da linguagem Java
- Java SE 10 API Javadocs