Eu li em muitos sites. Opcional deve ser usado apenas como um tipo de retorno e não usado em argumentos de método. Estou lutando para encontrar uma razão lógica. Por exemplo, eu tenho um pedaço de lógica que possui 2 parâmetros opcionais. Portanto, acho que faria sentido escrever minha assinatura de método assim (solução 1):
public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
// my logic
}
Muitas páginas da Web especificam Opcional não devem ser usadas como argumentos de método. Com isso em mente, eu poderia usar a seguinte assinatura de método e adicionar um comentário claro do Javadoc para especificar que os argumentos podem ser nulos, esperando que futuros mantenedores leiam o Javadoc e, portanto, sempre realizem verificações nulas antes de usar os argumentos (solução 2) :
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Como alternativa, eu poderia substituir meu método por quatro métodos públicos para fornecer uma interface melhor e tornar mais óbvio que p1 e p2 são opcionais (solução 3):
public int calculateSomething() {
calculateSomething(null, null);
}
public int calculateSomething(String p1) {
calculateSomething(p1, null);
}
public int calculateSomething(BigDecimal p2) {
calculateSomething(null, p2);
}
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Agora, tento escrever o código da classe que invoca esse pedaço de lógica para cada abordagem. Primeiro, recupero os dois parâmetros de entrada de outro objeto que retorna Optional
s e depois invoco calculateSomething
. Portanto, se a solução 1 for usada, o código de chamada ficaria assim:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);
se a solução 2 for usada, o código de chamada ficaria assim:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
se a solução 3 for aplicada, eu poderia usar o código acima ou o seguinte (mas é significativamente mais código):
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
if (p2.isPresent()) {
result = myObject.calculateSomething(p1, p2);
} else {
result = myObject.calculateSomething(p1);
}
} else {
if (p2.isPresent()) {
result = myObject.calculateSomething(p2);
} else {
result = myObject.calculateSomething();
}
}
Portanto, minha pergunta é: Por que é considerado uma prática ruim usar Optional
s como argumentos de método (consulte a solução 1)? Parece a solução mais legível para mim e torna mais óbvio que os parâmetros podem estar vazios / nulos para futuros mantenedores. (Estou ciente de que os designers Optional
pretendem que ele seja usado apenas como um tipo de retorno, mas não consigo encontrar razões lógicas para não usá-lo nesse cenário).
null
?Respostas:
Ah, esses estilos de codificação devem ser tomados com um pouco de sal.
Em geral: opcional unifica dois estados, que precisam ser desvendados. Portanto, é mais adequado para resultado do que para entrada, para a complexidade do fluxo de dados.
fonte
Optional
parâmetro representa um dos três estados: anull
Opcional, um não nuloOptional
comisPresent() == false
e um não nuloOptional
envolvendo um valor real.@NotNull Optional
.Optional
em tudo, então o estado 1 (null
Opcional) geralmente indica um erro de programação, assim que você poderia muito bem não lidar com isso (apenas deixá-lo jogar umNullPointerException
)O melhor post que vi sobre o assunto foi escrito por Daniel Olszewski :
fonte
null
ouOptional
.Quase não existem boas razões para não usar
Optional
como parâmetros. Os argumentos contra isso se baseiam em argumentos da autoridade (consulte Brian Goetz - seu argumento é que não podemos aplicar opcionais não nulos) ou que osOptional
argumentos podem ser nulos (essencialmente o mesmo argumento). Obviamente, qualquer referência em Java pode ser nula, precisamos incentivar regras sendo impostas pelo compilador, não a memória dos programadores (o que é problemático e não é escalável).Linguagens de programação funcional incentivam
Optional
parâmetros. Uma das melhores maneiras de usar isso é ter vários parâmetros opcionais eliftM2
usar uma função assumindo que os parâmetros não estejam vazios e retornando uma opção opcional (consulte http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ data / Option.html # liftM2-fj.F- ). Infelizmente, o Java 8 implementou uma biblioteca muito limitada, suportando opcional.Como programadores Java, devemos usar nulo apenas para interagir com bibliotecas herdadas.
fonte
@Nonnull
e@Nullable
do javax.annotation? Eu os uso extensivamente em uma biblioteca que desenvolvo, e o suporte fornecido pelo IDE (IntelliJ) é muito bom.@NonNull
é totalmente adequada se eles servem ao mesmo propósito de maneiras diferentes?" Para mim, há apenas um argumento que faz pelo menos algum sentido: "Opcional deve ser usado para fornecer APIs melhores, com um tipo de retorno claro. No entanto, para argumentos de método, funções sobrecarregadas podem ser usadas". - ainda assim, não acho que esse argumento seja suficientemente forte.Este conselho é uma variante da regra prática "seja o mais inespecífico possível em relação às entradas e o mais específico possível em relação às saídas".
Geralmente, se você possui um método que usa um valor não nulo simples, é possível mapeá-lo sobre o
Optional
, para que a versão simples seja estritamente mais inespecífica em relação às entradas. No entanto, existem várias razões possíveis pelas quais você desejaria exigir umOptional
argumento:Optional
Optional
se o valor especificado estiver vazioVocê achaOptional
tão incrível que quem usa sua API deve aprender sobre ela ;-)fonte
O padrão com
Optional
é para evitar o retornonull
. Ainda é perfeitamente possível passarnull
para um método.Embora ainda não sejam realmente oficiais, é possível usar anotações no estilo JSR-308 para indicar se você aceita ou não
null
valores na função. Observe que você precisaria ter as ferramentas certas para realmente identificá-lo e forneceria mais uma verificação estática do que uma política de tempo de execução aplicável, mas ajudaria.fonte
Optional
também não resolve o problema, daí a minha sugestão para as anotações.Isso me parece um pouco tolo, mas a única razão pela qual consigo pensar é que os argumentos do objeto nos parâmetros do método já são opcionais de certa forma - eles podem ser nulos. Portanto, forçar alguém a pegar um objeto existente e envolvê-lo em um opcional é meio inútil.
Dito isto, encadear métodos que aceitem / devolvam opcionais é uma coisa razoável a se fazer, por exemplo, talvez mônada.
fonte
Optional
é totalmente inútil?Confira o JavaDoc no JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , uma nota da API é adicionada:
fonte
Minha opinião é que o opcional deve ser uma mônada e eles não são concebíveis em Java.
Na programação funcional, você lida com funções de ordem pura e superior que recebem e compõem seus argumentos apenas com base em seu "tipo de domínio de negócios". A composição de funções que se alimentam ou cujo cálculo deve ser relatado para o mundo real (os chamados efeitos colaterais) requer a aplicação de funções que cuidam de descompactar automaticamente os valores das mônadas que representam o mundo externo (Estado, Configuração, Futuros, Talvez, Qualquer Um, Escritor, etc ...); isso é chamado de elevação. Você pode pensar nisso como uma espécie de separação de preocupações.
A mistura desses dois níveis de abstração não facilita a legibilidade, portanto é melhor evitá-la.
fonte
Outro motivo para ter cuidado ao passar um
Optional
parâmetro as é que um método deve fazer uma coisa ... Se você passar umOptional
parâmetro, pode ser a favor de mais de uma coisa, pode ser semelhante a passar um parâmetro booleano.fonte
if(param.isPresent())
não é a melhor abordagem para uso opcional, em vez disso você pode usar:param.forEach(() -> {...})
Aceitar Opcional como parâmetros causa quebra desnecessária no nível do chamador.
Por exemplo, no caso de:
Suponha que você tenha duas cadeias não nulas (ou seja, retornadas de algum outro método):
Você é obrigado a envolvê-los em Opcional, mesmo que saiba que eles não estão vazios.
Isso fica ainda pior quando você precisa compor com outras estruturas "mapeáveis", ou seja. Éteres :
ref:
ref. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749
fonte
Eu acho que é porque você normalmente escreve suas funções para manipular dados e, em seguida, eleva-as para o
Optional
uso demap
funções similares. Isso adiciona oOptional
comportamento padrão a ele. Obviamente, pode haver casos em que é necessário escrever sua própria função auxiliar que funcionaOptional
.fonte
Acredito que a ressonância de ser é que você deve primeiro verificar se o Opcional é ou não nulo e, em seguida, tentar avaliar o valor que ele envolve. Muitas validações desnecessárias.
fonte
Os opcionais não foram projetados para essa finalidade, conforme explicado de maneira bastante agradável por Brian Goetz .
Você sempre pode usar @Nullable para indicar que um argumento de método pode ser nulo. O uso de um opcional realmente não permite que você escreva a lógica do método com mais precisão.
fonte
Mais uma abordagem, o que você pode fazer é
Depois de criar uma função (neste caso, fornecedor), você poderá repassá-la como qualquer outra variável e poderá chamá-la usando
A idéia aqui é se você tem valor opcional ou não, serão detalhes internos da sua função e não estarão no parâmetro Pensar funciona quando pensar em opcional como parâmetro é, na verdade, uma técnica muito útil que encontrei.
Quanto à sua pergunta, se você deve ou não fazer isso com base em sua preferência, mas como outros disseram, torna sua API feia para dizer o mínimo.
fonte
No começo, eu também preferia passar Optionals como parâmetro, mas se você alternar de uma perspectiva de API-Designer para uma de API-User, verá as desvantagens.
Para o seu exemplo, onde cada parâmetro é opcional, sugiro alterar o método de cálculo para uma própria classe, como a seguir:
fonte
Eu sei que esta pergunta é mais sobre opinião do que fatos concretos. Mas recentemente mudei de desenvolvedor de .net para java, então ingressei apenas recentemente no grupo Opcional. Além disso, prefiro declarar isso como um comentário, mas, como meu nível de pontuação não me permite comentar, sou forçado a colocar isso como resposta.
O que venho fazendo, o que me serviu bem como regra geral. É usar Opcionais para tipos de retorno e usar Opcionais apenas como parâmetros, se eu exigir o valor do Opcional e o clima ou não, o Opcional possui um valor dentro do método.
Se eu me preocupo apenas com o valor, verifico isPresent antes de chamar o método, se eu tiver algum tipo de log ou lógica diferente no método que depende se o valor existir, passarei feliz pelo Opcional.
fonte
Isso ocorre porque temos requisitos diferentes para um usuário da API e um desenvolvedor da API.
Um desenvolvedor é responsável por fornecer uma especificação precisa e uma implementação correta. Portanto, se o desenvolvedor já estiver ciente de que um argumento é opcional, a implementação deve lidar com ele corretamente, seja ele nulo ou opcional. A API deve ser o mais simples possível para o usuário e nulo é o mais simples.
Por outro lado, o resultado é passado do desenvolvedor da API para o usuário. No entanto, a especificação é completa e detalhada, ainda há uma chance de o usuário desconhecer ou ter preguiça de lidar com ela. Nesse caso, o resultado opcional força o usuário a escrever um código extra para lidar com um possível resultado vazio.
fonte
Primeiro de tudo, se você estiver usando o método 3, poderá substituir as últimas 14 linhas de código por:
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
As quatro variações que você escreveu são métodos de conveniência . Você só deve usá-los quando for mais conveniente. Essa também é a melhor abordagem. Dessa forma, a API é muito clara sobre quais membros são necessários e quais não são. Se você não quiser escrever quatro métodos, poderá esclarecer as coisas pela maneira como nomeia seus parâmetros:
public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)
Dessa forma, fica claro que valores nulos são permitidos.
Seu uso
p1.orElse(null)
ilustra como o código fica detalhado ao usar o Opcional, o que é parte do motivo pelo qual eu o evito. Opcional foi escrito para programação funcional. Os córregos precisam disso. Seus métodos provavelmente nunca devem retornar Opcional, a menos que seja necessário usá-los na programação funcional. Existem métodos, comoOptional.flatMap()
método, que requerem uma referência a uma função que retorna Opcional. Aqui está a sua assinatura:public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)
Portanto, essa é geralmente a única boa razão para escrever um método que retorne Opcional. Mas mesmo lá, isso pode ser evitado. Você pode passar um getter que não retorna Opcional a um método como flatMap (), envolvendo-o em outro método que converte a função no tipo correto. O método wrapper é assim:
Então, suponha que você tenha um getter como este:
String getName()
Você não pode passá-lo para flatMap como este:
opt.flatMap(Widget::getName) // Won't work!
Mas você pode passar assim:
opt.flatMap(optFun(Widget::getName)) // Works great!
Fora da programação funcional, os opcionais devem ser evitados.
Brian Goetz disse isso melhor quando disse o seguinte:
A razão pela qual o Opcional foi adicionado ao Java é porque:
é mais limpo que isso:
fonte