"Java ainda não está morto - e as pessoas estão começando a descobrir isso."
Este tutorial usará o código anotado simples para descrever novos recursos e você não verá o texto de sucesso de bilheteria.
1. O método padrão da interface
O Java 8 nos permite adicionar uma implementação do método não-ABSTRAT à interface, basta usar a palavra-chave padrão.
A cópia do código é a seguinte:
Fórmula da interface {
calcular duplo (int a);
Double Sqrt padrão (int a) {
retornar math.sqrt (a);
}
}
Além de ter um método calculado, a interface da fórmula também define o método SQRT.
A cópia do código é a seguinte:
Fórmula de fórmula = new Formula () {
@Override
public duplo calcule (int a) {
retornar sqrt (a * 100);
}
};
Formula.Calcular (100);
Formula.sqrt (16);
A fórmula no artigo é implementada como uma instância de uma classe anônima. Na próxima seção, veremos uma abordagem mais simples para implementar uma interface de método único.
Nota do tradutor: Existe apenas herança única no Java. Em outros idiomas, o método de ter uma classe possui outro código reutilizável ao mesmo tempo é chamado de mixin. Este novo Java 8 Special está mais próximo da característica de Scala da perspectiva da implementação do compilador. Há também um conceito chamado método de extensão em C#, que permite que os tipos existentes sejam estendidos, que é semanticamente diferente disso no Java 8.
2. Expressões lambda
Primeiro, vamos dar uma olhada em como as cordas são organizadas na versão antiga de Java:
A cópia do código é a seguinte:
Lista <String> nomes = Arrays.asList ("Peter", "Anna", "Mike", "Xenia");
Coleções.sort (nomes, novo comparador <tring> () {
@Override
public int compare (string a, string b) {
return b.compareto (a);
}
});
Basta passar em um objeto de lista e um comparador com as coleções de métodos estáticos.Sort para organizá -lo na ordem especificada. Geralmente é feito para criar um objeto comparador anônimo e passá -lo para o método de classificação.
No Java 8, você não precisa usar esse método tradicional de objetos anônimos.
A cópia do código é a seguinte:
Coleções.sort (nomes, (string a, string b) -> {
return b.compareto (a);
});
Veja, o código se torna mais segmentado e legível, mas pode realmente ser escrito mais curto:
A cópia do código é a seguinte:
Coleções.sort (nomes, (string a, string b) -> b.compareto (a));
Para o corpo da função com apenas uma linha de código, você pode remover os aparelhos {} e retornar palavras -chave, mas você pode escrevê -lo mais curto:
A cópia do código é a seguinte:
Coleções.sort (nomes, (a, b) -> b.compareto (a));
O compilador Java pode deduzir automaticamente os tipos de parâmetros, para que você não precise escrever o tipo novamente. Em seguida, vamos ver o que as expressões Lambda mais convenientes podem fazer:
3. Interface funcional
Como as expressões lambda são representadas no sistema de tipo Java? Cada expressão lambda corresponde a um tipo, geralmente um tipo de interface. "Interface funcional" refere -se a uma interface que contém apenas um método abstrato, e cada expressão de lambda desse tipo será correspondida a esse método abstrato. Como o método padrão não é um método abstrato, você também pode adicionar métodos padrão à sua interface funcional.
Podemos tratar as expressões Lambda como qualquer tipo de interface que contém apenas um método abstrato para garantir que sua interface deve atender a esse requisito. Anotação, o compilador descobrirá que a interface marcada é anotada por esta anotação.
Exemplos são os seguintes:
A cópia do código é a seguinte:
@FunctionalInterface
conversor de interface <f, t> {
T converter (f de);
}
Converter <String, Integer> converter = (de) -> Integer.valueof (de);
Inteiro convertido = converter.convert ("123");
System.out.println (convertido);
Deve -se notar que, se o @FunctionInterface não for especificado, o código acima também estará correto.
A nota do tradutor mapeia as expressões lambda para uma interface de método único. Função O Intérprete Rhino será automaticamente uma instância de uma única interface para um adaptador de função.
4. Referência de método e construtor
O código na seção anterior também pode ser representado por referências de método estático:
A cópia do código é a seguinte:
Conversor <string, Integer> converter = Inteiro :: valueof;
Inteiro convertido = converter.convert ("123");
System.out.println (convertido);
O Java 8 permite que você use a palavra -chave :: para passar um método ou referência de construtor.
A cópia do código é a seguinte:
conversor = algo :: startSwith;
String convertido = converter.convert ("java");
System.out.println (convertido);
Vamos dar uma olhada em como os construtores são referenciados usando a palavra -chave :: primeiro, definimos uma classe simples com vários construtores:
A cópia do código é a seguinte:
classe de classe {
String primeiro nome;
String lastName;
Pessoa() {}
Pessoa (String FirstName, String LastName) {
this.FirstName = FirstName;
this.LastName = LastName;
}
}
Em seguida, especificamos uma interface de fábrica de objetos para criar objetos pessoais:
A cópia do código é a seguinte:
Interface PersonFactory <P estende a pessoa> {
P create (string primeironame, string lastName);
}
Aqui, usamos referências de construtor para associá -las em vez de implementar uma fábrica completa:
A cópia do código é a seguinte:
PersonFactory <Pesso> PERSONFACTION = PESOL :: novo;
Pessoa de pessoa = PersonFactory.Create ("Peter", "Parker");
Precisamos usar apenas a pessoa :: Novo para obter uma referência ao construtor da classe Pessoa.
5. Lambda Scope
A maneira de acessar escopos externos nas expressões Lambda é semelhante à das versões mais antigas de objetos anônimos. Você pode acessar diretamente as variáveis locais externas marcadas finais, ou campos e variáveis estáticas da instância.
6. Acesse variáveis locais
Podemos acessar variáveis locais externas diretamente nas expressões Lambda:
A cópia do código é a seguinte:
final int num = 1;
Converter <Inteiro, String> StringConverter =
(de) -> string.valueof (de + num);
StringConverter.Convert (2);
Mas, diferentemente dos objetos anônimos, o número variável aqui pode ser declarado como final sem declará -lo como final, e o código também está correto:
A cópia do código é a seguinte:
int num = 1;
Converter <Inteiro, String> StringConverter =
(de) -> string.valueof (de + num);
StringConverter.Convert (2);
No entanto, o número aqui não deve ser modificado pelo código subsequente (ou seja, implicitamente tem semântica final), por exemplo, o seguinte não pode ser compilado:
A cópia do código é a seguinte:
int num = 1;
Converter <Inteiro, String> StringConverter =
(de) -> string.valueof (de + num);
num = 3;
Tentar modificar o NUM nas expressões Lambda também não é permitido.
7. Campos de objetos de acesso e variáveis estáticas
Ao contrário das variáveis locais, os campos e as variáveis estáticas dentro do Lambda podem ser lidas e escritas. Esse comportamento é consistente com objetos anônimos:
Copie o código da seguinte forma: classe Lambda4 {
estático int OuterstaticNum;
int outternum;
void testscopes () {
Converter <Inteiro, String> StringConverter1 = (de) -> {
externo = 23;
return string.valueof (de);
};
Converter <Inteiro, String> StringConverter2 = (de) -> {
ExterstaticNum = 72;
return string.valueof (de);
};
}
}
8. O método padrão para acessar a interface
Lembre -se do exemplo de fórmula na primeira seção.
O método padrão não pode ser acessado na expressão lambda, e o código a seguir não será compilado:
A cópia do código é a seguinte:
Fórmula de fórmula = (a) -> sqrt (a * 100);
Interfaces funcionais embutidas
A API JDK 1.8 contém muitas interfaces funcionais internas, como interfaces comparador ou executável comumente usadas em Java antigo, e essas interfaces adicionaram @functionalInterface Anotation a ser usada em lambdas.
A API Java 8 também fornece muitas novas interfaces funcionais para tornar o trabalho mais conveniente.
Interface predicada
A interface de predicado possui apenas um parâmetro, que retorna o tipo booleano. Esta interface contém vários métodos padrão para combinar predicado em outras lógicas complexas (como: versus ou, não):
A cópia do código é a seguinte:
Predicado <string> predicado = (s) -> s.Length ()> 0;
previsto.test ("foo");
previc.negate (). Test ("foo");
Predicado <iOolean> Nonnull = Objetos :: NonNull;
Predicado <boolean> isnull = objetos :: isnull;
Predicado <string> isEmpty = string :: isEmpty;
Predicado <string> isNotEmpty = isEmpty.negate ();
Interface da função
A interface da função possui um parâmetro e retorna um resultado, e vem com alguns métodos padrão (compõe e depois) que podem ser combinados com outras funções:
A cópia do código é a seguinte:
Function <string, número inteiro> toInteger = inteiro :: valueof;
Function <string, string> backtoString = ToInteger.andThen (string :: valueof);
backtoString.Apply ("123");
Interface do fornecedor
A interface do fornecedor retorna um valor de qualquer tipo.
A cópia do código é a seguinte:
Fornecedor <sesso> Personsupplier = Person :: New;
Personsupplier.get ();
Interface do consumidor
A interface do consumidor representa a operação executada em um único parâmetro.
A cópia do código é a seguinte:
Consumidor <SOYSE> GERETER = (P) -> System.out.println ("Hello", + P.FirstName);
GERETER.ACECT (Nova pessoa ("Luke", "Skywalker"));
Interface do comparador
O comparador é uma interface clássica no Java Old, e o Java 8 adicionou uma variedade de métodos padrão:
A cópia do código é a seguinte:
Comparador <SOYSE> Comparator = (P1, P2) -> P1.FirstName.compareto (p2.firstName);
Pessoa P1 = nova pessoa ("John", "Doe");
Pessoa P2 = nova pessoa ("Alice", "País das Maravilhas");
comparador.comPare (P1, P2);
comparador.Reversed (). Compare (p1, p2);
Interface opcional
Opcional não é uma função, mas uma interface.
Opcional é definido como um recipiente simples cujo valor pode ser nulo ou não. Antes de Java 8, uma função geralmente deve retornar um objeto não vazio, mas ocasionalmente pode retornar nulo.
A cópia do código é a seguinte:
Opcional <string> opcional = opcional.of ("bam");
opcional.ispresent ();
opcional.get ();
opcional.orelse ("Fallback");
opcional.ifpresent (S) -> System.out.println (S.Charat (0)));
Interface de fluxo
java.util.stream representa uma sequência de operações que podem ser aplicadas a um conjunto de elementos ao mesmo tempo. As operações de fluxo são divididas em dois tipos: operações intermediárias ou operações finais. A criação do fluxo requer a especificação de uma fonte de dados, como uma subclasse de java.util.Collection, List ou Set, que não é suportado pelo mapa. As operações de fluxo podem ser executadas em série ou em paralelo.
Primeiro, vamos ver como o fluxo é usado.
A cópia do código é a seguinte:
List <string> stringCollection = new ArrayList <> ();
stringCollection.add ("ddd2");
stringCollection.add ("aaa2");
stringCollection.add ("bbb1");
stringCollection.add ("aaa1");
stringCollection.add ("bbb3");
StringCollection.add ("CCC");
stringCollection.add ("bbb2");
stringCollection.add ("ddd1");
O Java 8 estende a classe de coleta e pode criar um fluxo através da coleção.Stream () ou Coleção.paralLelsTream (). As seções a seguir explicarão as operações de fluxo comumente usadas em detalhes:
Filtro filtro
A filtragem é filtrada através de uma interface predicada e apenas os elementos que atendem aos critérios são mantidos. A foreach requer uma função para executar elementos filtrados em sequência. A foreach é uma operação final, portanto, não podemos executar outras operações de fluxo após a Foreach.
A cópia do código é a seguinte:
StringCollection
.fluxo()
.Filter ((s) -> s.startswith ("a"))
.ForEach (System.out :: println);
// "aaa2", "aaa1"
Classificar
O Sort é uma operação intermediária e o fluxo retornado após a classificação é retornado. Se você não especificar um comparador personalizado, o tipo padrão será usado.
A cópia do código é a seguinte:
StringCollection
.fluxo()
.Sorted ()
.Filter ((s) -> s.startswith ("a"))
.ForEach (System.out :: println);
// "aaa1", "aaa2"
Deve -se notar que a classificação cria apenas um fluxo organizado e não afetará a fonte de dados original.
A cópia do código é a seguinte:
System.out.println (StringCollection);
// DDD2, AAA2, BBB1, AAA1, BBB3, CCC, BBB2, DDD1
Mapeamento de mapas
O mapa de operação intermediário converte elementos em objetos adicionais em sequência de acordo com a interface de função especificada. Você também pode usar o mapa para converter objetos em outros tipos.
A cópia do código é a seguinte:
StringCollection
.fluxo()
.map (string :: touppercase)
.Sorted ((a, b) -> b.compareto (a))
.ForEach (System.out :: println);
// "ddd2", "ddd1", "ccc", "bbb3", "bbb2", "aaa2", "aaa1"
Corresponder
O fluxo fornece uma variedade de operações correspondentes, permitindo a detecção de um predicado especificado corresponde a todo o fluxo. Todas as operações correspondentes são operações finais e retornam um valor do tipo booleano.
A cópia do código é a seguinte:
boolean anystartswitha =
StringCollection
.fluxo()
.anymatch ((S) -> s.startswith ("a"));
System.out.println (Anystartswitha);
Boolean Allstartswitha =
StringCollection
.fluxo()
.AllMatch ((S) -> S.StartSwith ("A"));
System.out.println (Allstartswitha);
boolean não -iniciaSwithz =
StringCollection
.fluxo()
.NoneMatch ((S) -> S.StartSwith ("Z"));
System.out.println (NonEstartSwithz);
Contar
A contagem é uma operação final, que retorna o número de elementos no fluxo, e o tipo de valor de retorno é longo.
A cópia do código é a seguinte:
StartSwithB longo =
StringCollection
.fluxo()
.Filter ((s) -> s.startswith ("b"))
.contar();
System.out.println (StartSwithB);
Reduzir os regulamentos
Esta é uma operação final, permitindo que vários elementos no fluxo sejam definidos como um elemento através da função especificada, e o resultado da elegibilidade é representado pela interface opcional:
A cópia do código é a seguinte:
Opcional <string> reduzido =
StringCollection
.fluxo()
.Sorted ()
.Reduce ((S1, S2) -> S1 + "#" + S2);
reduzido.Ifpresent (System.out :: println);
// "aaa1#aaa2#bb1#bb2#bb3#ccc#ddd1#ddd2"
Fluxos paralelos
Como mencionado anteriormente, o fluxo possui dois tipos: serial e paralelo.
O exemplo a seguir mostra como melhorar o desempenho através do fluxo paralelo:
Primeiro, criamos uma tabela grande sem elementos duplicados:
A cópia do código é a seguinte:
int max = 1000000;
List <string> valores = new ArrayList <> (max);
for (int i = 0; i <max; i ++) {
Uuid uuid = uuid.randomuuid ();
valores.add (uuid.toString ());
}
Em seguida, calculamos quanto tempo leva para classificar esse fluxo.
Tipo em série:
A cópia do código é a seguinte:
long t0 = System.nanotime ();
long count = valores.stream (). classificado (). count ();
System.out.println (contagem);
T1 longo = System.Nanotime ();
Millis longo = timeUnit.nanoseconds.tomillis (T1 - T0);
System.out.println (string.format ("classificação sequencial levada: %d ms", milis));
// Tempo de série: 899 ms
Classificação paralela:
A cópia do código é a seguinte:
long t0 = System.nanotime ();
long count = valores.paralLelsTream (). classificada (). count ();
System.out.println (contagem);
T1 longo = System.Nanotime ();
Millis longo = timeUnit.nanoseconds.tomillis (T1 - T0);
System.out.println (string.format ("classificação paralela levada: %d ms", milis));
// classificação paralela leva tempo: 472 ms
Os dois códigos acima são quase os mesmos, mas a versão paralela é tão rápida quanto 50%.
Mapa
Como mencionado anteriormente, o tipo de mapa não suporta o fluxo, mas o MAP fornece alguns métodos novos e úteis para lidar com algumas tarefas diárias.
A cópia do código é a seguinte:
Mapa <número inteiro, string> map = new hashmap <> ();
for (int i = 0; i <10; i ++) {
map.putifabsent (i, "val" + i);
}
map.foreach ((id, val) -> system.out.println (val));
O código acima é fácil de entender.
O exemplo a seguir mostra outras funções úteis no mapa:
A cópia do código é a seguinte:
map.computeifpresent (3, (num, val) -> val + num);
map.get (3);
map.computeifpresent (9, (num, val) -> nulo);
Map.ContainsKey (9);
map.computeifabsent (23, num -> "val" + num);
Map.ContainsKey (23);
map.computeifabsent (3, num -> "bam");
map.get (3);
Em seguida, mostre como excluir um item no mapa que corresponda a todas as teclas:
A cópia do código é a seguinte:
map.remove (3, "val3");
map.get (3);
map.remove (3, "val33");
map.get (3);
Outro método útil:
A cópia do código é a seguinte:
map.getordefault (42, "não encontrado");
Também é fácil mesclar os elementos do mapa:
A cópia do código é a seguinte:
map.Merge (9, "val9", (valor, newValue) -> value.Concat (newValue));
map.get (9);
map.Merge (9, "concat", (valor, newValue) -> value.concat (newvalue));
map.get (9);
O que a Merge faz é inserir se o nome da chave não existir, caso contrário, mescla o valor correspondente à chave original e a reinsça no mapa.
9. Data API
O Java 8 contém um novo conjunto de APIs de tempo e data sob o pacote Java.Time. A nova API de data é semelhante à biblioteca Joda-Time de código aberto, mas não é exatamente o mesmo.
Relógio
A classe do relógio fornece um método para acessar a data e a hora atual. Um ponto específico no tempo também pode ser representado pela classe instantânea, que também pode ser usada para criar objetos antigos java.util.date.
A cópia do código é a seguinte:
Relógio = relógio.systemDefaultZone ();
longa millis = relógio.millis ();
Instant instant = relógio.Instant ();
Data Legacydate = Date.From (Instant);
Fuso horário dos fusos horários
Na nova API, o fuso horário é representado pelo ZoneID. O fuso horário pode ser facilmente obtido usando o método estático de. O fuso horário define a diferença de tempo no tempo do UTS, o que é extremamente importante ao converter entre o objeto de ponto no tempo instantâneo e o objeto de data local.
A cópia do código é a seguinte:
System.out.println (zoneId.getAvailableZoneIds ());
// Imprime todos os IDs de fuso horário disponíveis
Zoneid zone1 = zoneid.of ("Europa/Berlim");
Zoneid zone2 = zoneid.of ("Brasil/leste");
System.out.println (zone1.getRules ());
System.out.println (zone2.getRules ());
// zonerules [CurrentStandardOffset =+01: 00]
// zonerules [CurrentStandardOffset = -03: 00]
Horário local da hora local
O LocalTime define um tempo sem informações sobre funções horárias, como 22:00 ou 17:30:15. O exemplo a seguir cria dois tempos locais usando o fuso horário criado pelo código anterior. O tempo é então comparado e a diferença de horário entre as duas vezes é calculada em horas e minutos:
A cópia do código é a seguinte:
LocalTime Now1 = LocalTime.now (Zone1);
LocalTime agora2 = localTime.now (Zone2);
System.out.println (agora1.isbe antes (agora2));
longas horas entre = Chronounit.hours.between (agora1, agora2);
Minutos longos e Chronounit.minutes.Between (agora1, agora2);
System.out.println (HoursBetween);
System.out.println (Miniatsbetween);
A LocalTime fornece uma variedade de métodos de fábrica para simplificar a criação de objetos, incluindo a análise de tempo.
A cópia do código é a seguinte:
Localtime Late = LocalTime.of (23, 59, 59);
System.out.println (atrasado);
DATETIMEFORMATTER ALEMANformatter =
DATETIMEFORMATTER
.oflocalizedtime (formatStyle.short)
.Withlocale (Locale.German);
LocalTime Leettime = Localtime.parse ("13:37", AlemanFormatter);
System.out.println (LeetTime);
LocalDate Data Local
LocalDate representa uma data exata, como 2014-03-11. O valor desse objeto é imutável e é basicamente o mesmo que o local. O exemplo a seguir mostra como adicionar/luar/ano a um objeto de data. Observe também que esses objetos são imutáveis e a operação retorna uma nova instância.
A cópia do código é a seguinte:
LocalDate Today = localDate.now ();
LocalDate Tomorrow = Today.Plus (1, Chronounit.Days);
LocalDate ontem = amanhã.MinusDays (2);
LocalDate IndependeDENDAY = LOCALDATE.OF (2014, Month.July, 4);
DayofWeek DayOfWeek = IndependeDeenDay.GetdayofWeek ();
System.out.println (DayOfWeek);
Analisar um tipo de data local de uma string é tão simples quanto analisar a hora local:
A cópia do código é a seguinte:
DATETIMEFORMATTER ALEMANformatter =
DATETIMEFORMATTER
.OflocalizedDate (formatStyle.medium)
.Withlocale (Locale.German);
LOCALDATE XMAS = localDate.Parse ("24.12.2014", AlemanFormatter);
System.out.println (XMAS);
LocalDateTime Local DateTime
LocalDateTime representa tempo e data, o que é equivalente à mesclagem do conteúdo das duas primeiras seções em um objeto. LocalDateTime, como Localtime e LocalDate, são imutáveis. LocalDateTime fornece alguns métodos para acessar campos específicos.
A cópia do código é a seguinte:
LocalDateTime Sylvester = LocalDateTime.Of (2014, Month.DECEMBRO, 31, 23, 59, 59);
DayofWeek DayofWeek = Sylvester.GetdayofWeek ();
System.out.println (DayOfWeek);
Mês mês = sylvester.getmonth ();
System.out.println (mês);
Minuto longo do dia = sylvester.getlong (cronofield.minute_of_day);
System.out.println (MinuteOfday);
Basta anexar as informações do fuso horário e elas podem ser convertidas em um objeto instantâneo no ponto de tempo.
A cópia do código é a seguinte:
Instantâneo instantâneo = sylvester
.atZone (zoneId.systemDefault ())
.ToinStant ();
Data Legacydate = Date.From (Instant);
System.out.println (Legacydate);
A formatação do LocalDeTime é a mesma que a formatação e a data. Além de usar formatos predefinidos, também podemos definir o formato:
A cópia do código é a seguinte:
DATETIMEFORMATTER FORMATRE =
DATETIMEFORMATTER
.ofPattern ("mmm dd, yyyy - hh: mm");
LOCALDATETIME Parsed = LocalDateTime.Parse ("03 de novembro de 2014 - 07:13", Formatter);
String string = formatter.format (parsed);
System.out.println (String);
Ao contrário do java.text.numberFormat, a nova versão do DateTimeFormatter é imutável, por isso é segura para threads.
Informações detalhadas sobre o formato de hora e data: http://download.java.net/jdk8/docs/api/java/time/format/datetimeformatter.html
10. Notas de anotação
Várias anotações são suportadas no Java 8. Vamos primeiro olhar para um exemplo para entender o que isso significa.
Primeiro, defina uma anotação de dicas embaladas para colocar um conjunto específico de anotações de dica:
A cópia do código é a seguinte:
@interface dicas {
Dica [] value ();
}
@Repeatable (Hints.class)
@interface dica {
String value ();
}
O Java 8 nos permite usar anotações do mesmo tipo várias vezes, basta rotular a anotação @RePeatable.
Exemplo 1: Use a classe Wrapper como um contêiner para armazenar várias anotações (método antigo)
A cópia do código é a seguinte:
@Hints ({ @hint ("hint1"), @hint ("hint2")})
classe Pessoa {}
Exemplo 2: Usando várias anotações (novo método)
A cópia do código é a seguinte:
@Hint ("hint1")
@Hint ("hint2")
classe Pessoa {}
No segundo exemplo, o compilador Java definirá implicitamente a anotação @Hints para você.
A cópia do código é a seguinte:
Dica dica = pessoa.class.getannotation (dica.class);
System.out.println (dica);
Dicas dicas1 = PERSON.CLASS.GETANNOTATION (Hints.class);
System.out.println (hints1.value (). Length);
Dica [] dicas2 = PERSON.CLASS.GETANNOTATIONSBYTYPE (HINT.CLASS);
System.out.println (hints2.Length);
Mesmo se não definirmos a anotação @Hints na classe Pessoa, ainda podemos obter a anotação @Hints através do GetAnnotation (Hints.class).
Além disso, as anotações Java 8 foram adicionadas a dois novos alvos:
A cópia do código é a seguinte:
@Target ({elementType.type_parameter, elementType.type_use})
@interface myannotation {}
É tudo sobre os novos recursos do Java 8, e definitivamente há mais recursos esperando para serem descobertos. Existem muitas coisas úteis no JDK 1.8, como Arrays.parallelSort, StampedLock e CompletableFuture.