Usa para opcional


271

Tendo usado o Java 8 agora há mais de 6 meses, estou muito feliz com as novas alterações na API. Uma área em que ainda não estou confiante é quando usar Optional. Eu pareço variar entre querer usá-lo em qualquer lugar em que algo possa estar null, e em lugar nenhum.

Parece haver muitas situações em que eu poderia usá-lo, e nunca tenho certeza se isso traz benefícios (legibilidade / segurança nula) ou apenas causa sobrecarga adicional.

Portanto, tenho alguns exemplos e estaria interessado nos pensamentos da comunidade sobre se isso Optionalé benéfico.

1 - Como método de retorno público, digite quando o método poderia retornar null:

public Optional<Foo> findFoo(String id);

2 - Como parâmetro de método quando o parâmetro pode ser null:

public Foo doSomething(String id, Optional<Bar> barOptional);

3 - Como membro opcional de um bean:

public class Book {

  private List<Pages> pages;
  private Optional<Index> index;

}

4 - Entrada Collections:

Em geral, eu não penso:

List<Optional<Foo>>

adiciona qualquer coisa - especialmente porque se pode usar filter()para remover nullvalores, etc., mas há bons usos Optionalnas coleções?

Algum caso que eu perdi?


2
Um caso que acho útil é, por exemplo, se você tiver um mapa de substituição. Por exemplo Map<Character, String>. Se não há substituição posso usar isto: Optional.ofNullable(map.get(c)).orElse(String.valueOf(c)). Observe também que o opcional foi roubado da goiaba e possui uma sintaxe muito melhor:Optional.fromNullable(map.get(c)).or(String.valueOf(c));
fge

3
Além disso, nas coleções, bem, há coleções que não permitem valores nulos! Opcional se encaixa na conta aqui. E você pode .filter(Optional::absent)"valores nulos" sair
fge

2
@ fge Com toda a justiça, acho que o conceito de opcional é realmente proveniente do FP.
VH-NZZ

2
@fge não é melhor assim expresso getOrDefault()?
Jim Garrison

Respostas:


206

O ponto principal de Optionalé fornecer um meio para uma função retornar um valor para indicar a ausência de um valor de retorno. Veja esta discussão . Isso permite que o chamador continue uma cadeia de chamadas de método fluentes.

Isso se aproxima mais do caso de uso nº 1 da pergunta do OP. Embora a ausência de um valor seja uma formulação mais precisa que nula, uma vez que algo como IntStream.findFirstnunca poderia retornar nulo.

Para o caso de uso # 2 , passando um argumento opcional para um método, isso pode funcionar, mas é um pouco desajeitado. Suponha que você tenha um método que use uma sequência seguida por uma segunda sequência opcional. Aceitar um Optionalcomo o segundo argumento resultaria em código como este:

foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());

Mesmo aceitar nulo é melhor:

foo("bar", "baz");
foo("bar", null);

Provavelmente, o melhor é ter um método sobrecarregado que aceite um argumento de cadeia única e forneça um padrão para o segundo:

foo("bar", "baz");
foo("bar");

Isso tem limitações, mas é muito melhor do que qualquer um dos itens acima.

Os casos de uso 3 e 4 , com um Optionalcampo de classe ou estrutura de dados, são considerados um uso indevido da API. Primeiro, vai contra o objetivo principal do design, Optionalconforme indicado na parte superior. Segundo, não agrega nenhum valor.

Existem três maneiras de lidar com a ausência de um valor em um Optional: fornecer um valor substituto, chamar uma função para fornecer um valor substituto ou lançar uma exceção. Se você estiver armazenando em um campo, faça isso no momento da inicialização ou da atribuição. Se você estiver adicionando valores a uma lista, como o OP mencionado, você tem a opção adicional de simplesmente não adicionar o valor, "achatando" os valores ausentes.

Tenho certeza de que alguém poderia apresentar alguns casos inventados em que realmente deseja armazenar Optionalum campo ou uma coleção, mas, em geral, é melhor evitar fazer isso.


47
Eu discordo do # 3. Pode ser útil ter Optionalum campo quando as coisas devem ser feitas ou não na presença ou ausência de um valor.
glglgl

15
Não vejo por que fazer if(foo == null)internamente seria melhor do que apenas armazenar o campo como optional. E não vejo por que ligar explicitamente getOptionalFoo()internamente é melhor do que apenas armazenar o campo como um Optional. Além disso, se um campo pode ser nulle um campo não pode ser null, por que não comunicar isso ao compilador, criando-o Optionalou não Optional.
mogronalol 24/03

27
@StuartMarks, essa é uma das coisas que me deixa perplexo quando ouço de todos os Optional<>especialistas: "não guarde Optional<>em um campo". Estou realmente tentando entender o objetivo desta recomendação. Considere uma árvore DOM em que um nó possa ter um pai ou não. Parece no caso de uso que Node.getParent()retornaria Optional<Node>. Devo realmente armazenar um nulle agrupar o resultado toda vez? Por quê? O que essa ineficiência extra me compra, além de tornar meu código feio?
Garret Wilson

7
@GarretWilson Por outro lado, suponha que sim Optional<Node> getParent() { return Optional.ofNullable(parent); }. Isso parece caro, porque aloca um Optionaltoda vez! Mas se o chamador descompactá-lo imediatamente, o objeto é extremamente curto e nunca é promovido fora do espaço eden. Pode até ser eliminado pela análise de escape do JIT. A resposta final é "depende", como sempre, mas o uso Optionalem campos potencialmente incha o uso da memória e diminui a velocidade da travessia da estrutura de dados. E, finalmente, acho que isso atrapalha o código, mas isso é uma questão de gosto.
Stuart Marks

6
@GarretWilson Então você escreveu um blog para isso? Eu estaria interessado em que :)
akhil_mittal

78

Estou atrasado para o jogo, mas para o que vale a pena, quero adicionar meus 2 centavos. Eles vão contra o objetivo do projetoOptional , que é bem resumido pela resposta de Stuart Marks , mas ainda estou convencido de sua validade (obviamente).

Use opcional em qualquer lugar

Em geral

Eu escrevi um postOptional inteiro sobre o uso, mas basicamente se resume a isso:

  • crie suas aulas para evitar a opção sempre que possível
  • em todos os casos restantes, o padrão deve ser usar em Optionalvez denull
  • possivelmente faça exceções para:
    • variáveis ​​locais
    • retornar valores e argumentos para métodos privados
    • blocos de código críticos de desempenho (sem suposições, use um criador de perfil)

As duas primeiras exceções podem reduzir a sobrecarga percebida das referências de quebra e descompactação Optional. Eles são escolhidos de forma que um nulo nunca possa legalmente passar um limite de uma instância para outra.

Observe que isso quase nunca permitirá Optionals em coleções, o que é quase tão ruim quanto nulls. Apenas não faça isso. ;)

Em relação às suas perguntas

  1. Sim.
  2. Se sobrecarregar não for uma opção, sim.
  3. Se outras abordagens (subclasse, decoração, ...) não são uma opção, sim.
  4. Por favor não!

Vantagens

Fazer isso reduz a presença de nulls na sua base de código, embora não os erradique. Mas esse nem é o ponto principal. Existem outras vantagens importantes:

Esclarece a intenção

O uso Optionalexpressa claramente que a variável é, bem, opcional. Qualquer leitor do seu código ou consumidor da sua API será surpreendido com o fato de que talvez não haja nada lá e que uma verificação seja necessária antes de acessar o valor.

Remove incerteza

Sem Optionalo significado de uma nullocorrência não é claro. Pode ser uma representação legal de um estado (consulte Map.get) ou um erro de implementação, como uma inicialização ausente ou com falha.

Isso muda drasticamente com o uso persistente de Optional. Aqui, a ocorrência de já nullsignifica a presença de um bug. (Como se o valor estivesse faltando, um Optionalteria sido usado.) Isso torna a depuração de uma exceção de ponteiro nulo muito mais fácil, pois a pergunta do significado disso nulljá está respondida.

Mais verificações nulas

Agora que nada pode ser nullmais, isso pode ser aplicado em qualquer lugar. Seja com anotações, asserções ou verificações simples, você nunca precisa pensar se esse argumento ou esse tipo de retorno pode ser nulo. Não pode!

Desvantagens

Claro, não há bala de prata ...

atuação

A quebra de valores (especialmente primitivos) em uma instância extra pode prejudicar o desempenho. Em laços apertados, isso pode se tornar perceptível ou até pior.

Observe que o compilador pode contornar a referência extra para vidas úteis de Optionals. No Java 10, os tipos de valor podem reduzir ainda mais ou remover a penalidade.

Serialização

Optionalnão é serializável, mas uma solução alternativa não é muito complicada.

Invariância

Devido à invariância de tipos genéricos em Java, certas operações se tornam complicadas quando o tipo de valor real é empurrado para um argumento de tipo genérico. Um exemplo é dado aqui (consulte "Polimorfismo paramétrico") .


Outra desvantagem (ou limitação) é que você não pode classificar instâncias opcionais (xxx), embora a Oracle tenha claramente imposto essa restrição deliberadamente. Eu sou cético quanto à sabedoria de ter essas classes disponíveis, onde elas devem ser usadas apenas em circunstâncias específicas, e supondo que o desenvolvedor faça a coisa certa. Resolver um problema pode causar outro. Talvez deva haver avisos do compilador para ajudar o desenvolvedor ao usar as classes Optional (xxx) de maneiras "indesejáveis" (por exemplo, como parâmetros de método, em coleções, em construtores, etc.) se isso realmente não for o uso pretendido?
Skomisa

Cerca de 4, opcionais em coleções: às vezes é realmente útil armazenar opcionais em Lists. Este é o caso quando é importante que um determinado elemento esteja localizado em um determinado índice, mas o elemento pode estar presente ou não. Isso é claramente comunicado por um List<Optional<T>>tipo. Se, por outro lado, é importante apenas quais objetos são membros de alguma coleção, então não faz sentido.
22716

3
Eu acho que o caso de uso 2 ainda é muito questionável. Um Optionalser passado para um método pode ser null. Então, o que você ganhou? Agora você tem duas verificações a fazer. Veja stackoverflow.com/a/31923042/650176
David V

2
O @DavidV olha a lista de vantagens que eu dei - todas elas se aplicam a esse caso também. Além disso, se você for all in Optional(o que deve ser o caso se você estiver disposto a passar como argumento) nullnunca é um valor legal. Portanto, chamar um método com um parâmetro explícito nullé obviamente um erro.
Nicolai

28

Pessoalmente, eu prefiro usar a Ferramenta de Inspeção de Código do IntelliJ para usar @NotNulle @Nullableverificações, pois elas são em grande parte tempo de compilação (podem ter algumas verificações em tempo de execução). Não é tão rigoroso quanto usar o Opcional, no entanto, essa falta de rigor deve ser apoiada por testes de unidade decentes.

public @Nullable Foo findFoo(@NotNull String id);

public @NotNull Foo doSomething(@NotNull String id, @Nullable Bar barOptional);

public class Book {

  private List<Pages> pages;
  private @Nullable Index index;

}

List<@Nullable Foo> list = ..

Isso funciona com o Java 5 e não há necessidade de quebrar e desembrulhar valores. (ou crie objetos wrapper)


7
Uhm, são essas anotações da IDEA? Eu prefiro JSR 305 do @Nonnullpessoalmente - Trabalha com FindBugs;)
fge

@fge Há uma falta de normas a este respeito, mas acredito que as ferramentas são geralmente configurável e você não tem que acabar com@Nonnull @NotNull etc
Peter Lawrey

4
Sim, isso é verdade; IDEA (13.x) dá três opções diferentes ... Meh, eu sempre acabam usando JSR 305 anywa
fge

3
Eu sei que isto é antiga, mas a discussão sobre anotações e ferramentas merece um link para stackoverflow.com/questions/35892063/... - que btw aborda especificamente o caso do Java 8.
Stephan Herrmann

25

Eu acho que o Guava Optional e sua página wiki o colocam muito bem:

Além do aumento da legibilidade resultante do nome nulo, a maior vantagem do Opcional é a sua prova de idiotice. Obriga você a pensar ativamente no caso ausente, se quiser que seu programa seja compilado, pois é necessário desembrulhar ativamente o Opcional e abordar esse caso. O nulo facilita muito o esquecimento de coisas e, embora o FindBugs ajude, não achamos que ele resolva o problema da mesma maneira.

Isso é especialmente relevante quando você retorna valores que podem ou não estar "presentes". É provável que você (e outros) se esqueça de que other.method (a, b) pode retornar um valor nulo do que é provável que esqueça que a poderia ser nulo ao implementar other.method. Retornar Opcional torna impossível para os chamadores esquecerem esse caso, pois precisam desembrulhar o objeto para que o código seja compilado. - (Fonte: Guava Wiki - Usando e evitando null - Qual é o objetivo? )

Optionalacrescenta alguma sobrecarga, mas acho que sua clara vantagem é explicitar que um objeto pode estar ausente e fazer com que os programadores lidem com a situação. Impede que alguém esqueça o != nullcheque amado .

Tomando o exemplo de 2 , acho que é um código muito mais explícito escrever:

if(soundcard.isPresent()){
  System.out.println(soundcard.get());
}

do que

if(soundcard != null){
  System.out.println(soundcard);
}

Para mim, o Optionalmelhor captura o fato de que não há placa de som presente.

Meus 2 ¢ sobre seus pontos:

  1. public Optional<Foo> findFoo(String id);- Eu não tenho certeza disso. Talvez eu devesse retornar um Result<Foo>que possa estar vazio ou conter um Foo. É um conceito semelhante, mas não é realmente um Optional.
  2. public Foo doSomething(String id, Optional<Bar> barOptional);- Eu preferiria o @Nullable e um findbugs, como na resposta de Peter Lawrey - veja também esta discussão .
  3. Seu exemplo de livro - não tenho certeza se usaria o Opcional internamente, isso pode depender da complexidade. Para a "API" de um livro, eu usaria um Optional<Index> getIndex()para indicar explicitamente que o livro pode não ter um índice.
  4. Eu não o usaria em coleções, em vez de não permitir valores nulos em coleções

Em geral, eu tentaria minimizar a passagem de nulls. (Uma vez queimado ...) Acho que vale a pena encontrar as abstrações apropriadas e indicar aos colegas programadores o que realmente representa um certo valor de retorno.


15
Você escreveria soundcard.ifPresent(System.out::println). Chamar isPresenté semanticamente o mesmo que procurar null.
um melhor oliver

O problema para mim é que coisas com tipos opcionais ainda podem ser nulos. Então, para estar completamente seguro, você precisa fazer algo assim if(soundcard != null && soundcard.isPresent()). Embora fazer uma API que retorne um Opcional e também possa retornar nulo seja algo que espero que ninguém faça.
Simon Baumgardt-Wellander

"Obriga você a pensar ativamente sobre o caso ausente, se quiser que seu programa seja compilado, já que é necessário desembrulhar ativamente o opcional e abordar esse caso". Não tenho muita certeza de como isso força isso, embora não trabalhe muito em Java. Você poderia explicar o que o Java faz para forçá-lo a desembrulhar E verificar o caso! IsPresent simplesmente para compilar?
esmagar

15

No tutorial do Oracle :

O objetivo do Opcional não é substituir todas as referências nulas da sua base de código, mas ajudar a criar APIs melhores nas quais - apenas lendo a assinatura de um método - os usuários podem dizer se devem esperar um valor opcional. Além disso, o Opcional obriga a desembrulhar ativamente um Opcional para lidar com a ausência de um valor; como resultado, você protege seu código contra exceções não desejadas de ponteiro nulo.


3
APIs apenas para a parte de retorno do método, certo? Os opcionais não devem ser usados ​​como parâmetros devido ao apagamento do tipo? às vezes eu uso um opcional opcionalmente em um método, convertido de um parâmetro anulável ou como um campo inicializado de um parâmetro de construtor nulo ... ainda tentando descobrir se isso é mais útil do que apenas deixá-lo como nulo - alguém tem alguma opinião?
ycomp 27/12/16

@ycomp Você pode encontrar muitas informações sobre esses casos nesta ou naquela pergunta. As respostas a essas perguntas contam muito mais do que o solicitado e abrangem outros casos. Para obter mais informações, navegue mais ou faça sua própria pergunta, porque provavelmente você não receberá muita atenção aqui. ; )
ctomek

8

1 - Como um método de retorno de método público, quando o método pode retornar nulo:

Aqui está um bom artigo que mostra a utilidade do caso de usuário nº 1. Existe esse código

...
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        Country country = address.getCountry();
        if (country != null) {
            String isocode = country.getIsocode();
            isocode = isocode.toUpperCase();
        }
    }
}
...

é transformado para isso

String result = Optional.ofNullable(user)
  .flatMap(User::getAddress)
  .flatMap(Address::getCountry)
  .map(Country::getIsocode)
  .orElse("default");

usando Opcional como um valor de retorno dos respectivos métodos getter .


2

Aqui está um uso interessante (acredito) para ... Testes.

Pretendo testar fortemente um de meus projetos e, portanto, construo asserções; só há coisas que tenho que verificar e outras não.

Portanto, construo coisas para afirmar e uso uma declaração para verificá-las, assim:

public final class NodeDescriptor<V>
{
    private final Optional<String> label;
    private final List<NodeDescriptor<V>> children;

    private NodeDescriptor(final Builder<V> builder)
    {
        label = Optional.fromNullable(builder.label);
        final ImmutableList.Builder<NodeDescriptor<V>> listBuilder
            = ImmutableList.builder();
        for (final Builder<V> element: builder.children)
            listBuilder.add(element.build());
        children = listBuilder.build();
    }

    public static <E> Builder<E> newBuilder()
    {
        return new Builder<E>();
    }

    public void verify(@Nonnull final Node<V> node)
    {
        final NodeAssert<V> nodeAssert = new NodeAssert<V>(node);
        nodeAssert.hasLabel(label);
    }

    public static final class Builder<V>
    {
        private String label;
        private final List<Builder<V>> children = Lists.newArrayList();

        private Builder()
        {
        }

        public Builder<V> withLabel(@Nonnull final String label)
        {
            this.label = Preconditions.checkNotNull(label);
            return this;
        }

        public Builder<V> withChildNode(@Nonnull final Builder<V> child)
        {
            Preconditions.checkNotNull(child);
            children.add(child);
            return this;
        }

        public NodeDescriptor<V> build()
        {
            return new NodeDescriptor<V>(this);
        }
    }
}

Na classe NodeAssert, faço isso:

public final class NodeAssert<V>
    extends AbstractAssert<NodeAssert<V>, Node<V>>
{
    NodeAssert(final Node<V> actual)
    {
        super(Preconditions.checkNotNull(actual), NodeAssert.class);
    }

    private NodeAssert<V> hasLabel(final String label)
    {
        final String thisLabel = actual.getLabel();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is null! I didn't expect it to be"
        ).isNotNull();
        assertThat(thisLabel).overridingErrorMessage(
            "node's label is not what was expected!\n"
            + "Expected: '%s'\nActual  : '%s'\n", label, thisLabel
        ).isEqualTo(label);
        return this;
    }

    NodeAssert<V> hasLabel(@Nonnull final Optional<String> label)
    {
        return label.isPresent() ? hasLabel(label.get()) : this;
    }
}

O que significa que a declaração realmente só é acionada se eu quiser verificar o rótulo!


2

OptionalA classe permite evitar o uso nulle fornecer uma alternativa melhor:

  • Isso incentiva o desenvolvedor a verificar a presença, a fim de evitar as não capturadas NullPointerException.

  • A API fica melhor documentada porque é possível ver onde esperar os valores que podem estar ausentes.

Optionalfornece API conveniente para trabalhos futuros com o objeto isPresent():; get(); orElse(); orElseGet(); orElseThrow(); map(); filter(); flatmap().

Além disso, muitas estruturas usam ativamente esse tipo de dados e o retornam da API.


2

Em java, apenas não os use, a menos que você seja viciado em programação funcional.

Eles não têm lugar como argumentos de método (eu prometo que alguém um dia passará para você um opcional nulo, não apenas um opcional que está vazio).

Eles fazem sentido para os valores de retorno, mas convidam a classe cliente a continuar esticando a cadeia de construção de comportamento.

FP e cadeias têm pouco espaço em uma linguagem imperativa como o java, porque torna muito difícil depurar, não apenas ler. Quando você pisa na linha, não pode conhecer o estado nem a intenção do programa; você precisa descobrir isso (em código que geralmente não é seu e muitos quadros de pilha com profundidade, apesar dos filtros de etapas) e precisa adicionar muitos pontos de interrupção para garantir que ele pare no código / lambda que você adicionou, em vez de simplesmente caminhar pelas linhas triviais if / else /.

Se você deseja programação funcional, escolha algo além de java e espere ter as ferramentas para depurar isso.


1

Eu não acho que opcional seja um substituto geral para métodos que potencialmente retornam valores nulos.

A idéia básica é: A ausência de um valor não significa que ele esteja potencialmente disponível no futuro. É uma diferença entre findById (-1) e findById (67).

A principal informação dos opcionais para o chamador é que ele pode não contar com o valor fornecido, mas pode estar disponível em algum momento. Talvez ele desapareça novamente e volte mais tarde mais uma vez. É como um interruptor de ligar / desligar. Você tem a "opção" de ligar ou desligar a luz. Mas você não tem opção se não tiver uma luz para acender.

Por isso, acho muito confuso introduzir Opcionais em todos os lugares onde anteriormente nulo era potencialmente retornado. Ainda utilizarei nulo, mas apenas em áreas restritas, como a raiz de uma árvore, inicialização lenta e métodos explícitos de localização.


0

Parece Optionalsó é útil se o tipo T em Opcional é um tipo primitivo, como int, long, char, etc. Para as classes "reais", não faz sentido para mim como você pode usar um nullvalor de qualquer maneira.

Eu acho que foi tirado daqui (ou de outro conceito de linguagem similar).

Nullable<T>

Em C #, isso Nullable<T>foi introduzido há muito tempo para agrupar tipos de valor.


2
Na Optionalverdade, é uma imitação da goiaba
fge

2
@fge OK, mas quando isso estava presente em C # (2005, MS.NET 2.0), não havia goiaba, eu acho. E ... quem sabe de onde o C # tirou isso.
Peter.petrov

3
@fge "Ripoff of Guava's Optional" é uma maneira engraçada de colocar isso, já que os caras da Guava participaram de discussões, forneceram suas experiências e geralmente eram a favor java.util.Optional.
Stuart Marks

6
@fge Sem dúvida, as APIs de Java foram fortemente influenciadas pelas Guava's. Estou tendo problemas com o "roubo", que parece roubar. Os caras goiaba contribuíram um monte de idéias valiosas para Java 8.
Stuart Marcas

6
Você está perdendo o ponto. Opcional deve ser usado em vez de retornar nulo. É mais seguro do que lançar potencialmente uma NullPointerException. Veja: oracle.com/technetwork/articles/java/…
Kilizo

0

Um possui semântica semelhante a uma instância não modificável do padrão de design do Iterator :Optional

  • pode ou não se referir a um objeto (conforme fornecido por isPresent())
  • pode ser desreferenciada (usando get()) se se referir a um objeto
  • mas não pode ser avançado para a próxima posição na sequência (não possui next()método).

Portanto, considere retornar ou passar um Optionalem contextos nos quais você poderia ter considerado anteriormente usar um Java Iterator.


-1

O Java SE 8 apresenta uma nova classe chamada java.util.Optional,

Você pode criar um opcional ou opcional vazio com valor nulo.

Optional<String> emptyOptional = Optional.empty(); 

E aqui está um opcional com um valor não nulo:

String valueString = new String("TEST");
Optional<String> optinalValueString = Optional.of(valueString );

Faça algo se um valor estiver presente

Agora que você possui um objeto Opcional, pode acessar os métodos disponíveis para lidar explicitamente com a presença ou ausência de valores. Em vez de ter que se lembrar de fazer uma verificação nula, da seguinte maneira:

String nullString = null;
if (nullString != null) {
    System.out.println(nullString);
}

Você pode usar o método ifPresent (), da seguinte maneira:

Optional<String> optinalString= null;
optinalString.ifPresent(System.out::println);

package optinalTest;

import java.util.Optional;

public class OptionalTest {
    public Optional<String> getOptionalNullString() {
        return null;
//      return Optional.of("TESt");
    }

    public static void main(String[] args) {

        OptionalTest optionalTest = new OptionalTest();

        Optional<Optional<String>> optionalNullString = Optional.ofNullable(optionalTest.getOptionalNullString());

        if (optionalNullString.isPresent()) {
            System.out.println(optionalNullString.get());
        }
    }
}

este copypaste pretendia ganhar reputação barata, mas não relacionado às perguntas do OP
stinger
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.