Sintaxe Java - Java syntax

Um snippet de código Java com palavras-chave destacadas em negrito na fonte azul

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 inttipo por padrão, a menos que o longtipo seja especificado anexando Lou lsufixo 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 throwe do Java catch.

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 mainmétodo. Pode haver mais de uma classe com um mainmétodo, mas a classe principal é sempre definida externamente (por exemplo, em um arquivo de manifesto ). O método deve ser statice 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 InputStreamque está localizada no pacote java.io.

Um pacote é declarado no início do arquivo com a packagedeclaração:

package myapplication.mylibrary;

public class MyClass {
}

As classes com o publicmodificador 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.MyClassterá 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 deleteoperador 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();

ifdeclaração pode incluir elsebloco 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 switchinstruções.

Os valores possíveis são listados usando caseró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 defaultró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 breakpalavra - 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 yieldafirmaçã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 whileloop, o teste é feito antes de cada iteração.

while (i < 10) {
    doSomething();
}

do ... while ciclo

No do ... whileloop, 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

forloops 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();
}

forLoop aprimorado

Os forloops 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 breake continue. Observe que a gotopalavra-chave Java não pode ser usada para pular para pontos específicos no código.

start:
someMethod();

break demonstração

A breakdeclaração sai do loop ou switchdeclaraçã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 continueinstrução interrompe a iteração atual da instrução de controle atual e começa a próxima iteração. O seguinte whileloop 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 continuedeclarações e breakdeclaraçõ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 returninstrução é usada para encerrar a execução do método e retornar um valor. Um valor retornado pelo método é escrito após a returnpalavra - chave. Se o método retornar qualquer coisa menos void, ele deve usar a returninstruçã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;
}

returninstrução termina a execução imediatamente, exceto em um caso: se a instrução for encontrada dentro de um trybloco e for complementada por a finally, o controle é passado para o finallybloco.

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... catchblocos.

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 trybloco são executadas e, se alguma delas lançar uma exceção, a execução do bloco é interrompida e a exceção é tratada pelo catchbloco. Pode haver vários catchblocos, 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 catchbloco 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 catchbloco correspondente seja encontrado em um dos métodos ativos no momento. Se a exceção se propagar até o mainmétodo mais superior sem que um catchbloco correspondente seja encontrado, uma descrição textual da exceção é gravada no fluxo de saída padrão.

As instruções dentro do finallybloco são sempre executadas após os blocos trye catch, independentemente de uma exceção ter sido lançada ou não e mesmo que uma returninstrução tenha sido alcançada. Esses blocos são úteis para fornecer código de limpeza que é garantido que sempre será executado.

Os blocos catche finallysão opcionais, mas pelo menos um ou outro deve estar presente após o trybloco.

try-with-resources demonstrativos

tryAs instruções -with-resources são um tipo especial de try-catch-finallyinstruções introduzidas como uma implementação do padrão dispose em Java SE 7. Em uma tryinstrução -with-resources a trypalavra-chave é seguida pela inicialização de um ou mais recursos que são liberados automaticamente quando a tryexecução do bloco está terminado. Os recursos devem ser implementados java.lang.AutoCloseable. tryAs instruções -with-resources não precisam ter um bloco catchou finallyao contrário das try-catch-finallyinstruçõ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 throwinstruçã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 throwinstruçã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 synchronizedinstrução está incluída na linguagem Java.

Para sincronizar um bloco de código, ele é precedido pela synchronizedpalavra - 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

assertdeclaraçõ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 assertpalavra-chave é usada seguida por uma expressão condicional. Se for avaliado falsequando 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 chartipo, 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'

charnã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 charvalores.

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 é nullquando 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 newoperador 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 staticmodificador.

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 como finalnã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 publicpodem 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
Esta imagem descreve o escopo do membro da classe em classes e pacotes.

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 staticpalavra-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 throwspalavra-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. Para synchronizedmétodos, o monitor é a instância da classe ou java.lang.Classse 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 declarado volatile, é 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 extendspalavra - chave. Uma classe pode fazer referência a si mesma usando a thispalavra - chave e sua superclasse direta usando a superpalavra - chave.

class Foo {

}

class Foobar extends Foo {

}

Se uma classe não especifica sua superclasse, ela herda implicitamente da java.lang.Objectclasse. Portanto, todas as classes em Java são subclasses de Objectclasse.

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 finalmé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,, strictfpque tem o mesmo efeito que para as classes, e também staticdesde 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 implementspalavra - chave. É permitido implementar mais de uma interface, caso em que são escritas após a implementspalavra-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 @FunctionalInterfaceanotaçã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 returninstruçã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 runCalculationpodem 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 interfacepalavra-chave é precedida pelo @sinal. Todas as anotações são implicitamente estendidas java.lang.annotation.Annotatione 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 defaultpalavra - 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 extendspalavra - 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 extendspalavra - chave ou a superpalavra - 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

links externos