Por que o Java tem métodos `void`?

51

/ Por que o Java precisa ter voidmétodos? Referência :

Qualquer método declarado nulo não retorna um valor.

Tanto quanto posso pensar, todo uso de voidseria melhor se retornássemos uma flag de status, o objeto que está sendo chamado ou null.

Isso tornaria cada chamada uma declaração que é atribuível e facilitaria os padrões do construtor e o encadeamento de métodos. Métodos que são chamados apenas para seus efeitos geralmente retornam um tipo booleano ou genérico Successou lançam uma exceção em caso de falha.

marisbest2
fonte
Para que não precisemos de uma sintaxe especial para escrever sub-rotinas. Podemos usar as mesmas funções que usamos.
candied_orange
Relevante
jpmc26

Respostas:

158

Como existe uma diferença entre "Esta função pode ter êxito ou falhar e é autoconsciente o suficiente para saber a diferença" e "Não há comentários sobre o efeito dessa função". Sem isso void, você verifica incessantemente os códigos de sucesso e acredita que está escrevendo um software robusto, quando na verdade não está fazendo nada disso.

Kilian Foth
fonte
10
Mas se foi bem-sucedido ou falhou, é algo a ser expresso lançando ou não uma exceção, não retornando uma bandeira. E retornar voidnão diz nada sobre se o método ou função é autoconsciente o suficiente para ser lançado conforme apropriado.
Volker Siegel
12
@ VolkerSiegel: O contexto da resposta é a pergunta. O OP sugeriu que, em todos os casos em que void é usado, a função retorne um sinalizador de status indicando sucesso da falha. Nesse contexto, retornar nulo realmente diz que a função literalmente não tem nada para retornar. Forçar essa função a sempre retornar 0 para indicar sucesso dará aos usuários da função uma falsa confiança de que estão verificando erros quando, na realidade, o valor de retorno é sempre 0, independentemente do sucesso ou falha.
slebetman
19
@VolkerSiegel Há um grande número de situações em que as exceções não são a maneira correta de fazer as coisas. Na verdade, eu diria que é mais raro uma exceção estar certa do que errada - uma exceção significa que algo horrível aconteceu que precisa ser levado em consideração. Um caso de falha esperado nunca deve usar uma exceção.
Gabe Séchan
35
@GabeSechan Não, exceções devem ser lançadas se o método não puder cumprir seu contrato. Se o método requer uma referência não nula, mas obtém uma, é uma falha esperada e deve ser lançada.
Andy
5
Existem muitas sintaxes alternativas para corrigir isso. A razão pela qual eles escolheram essa solução (estranha) é porque C a emprega.
Marco van de Voort
95
  1. Porque C tem um voidtipo e Java foi projetado para seguir muitas das convenções da família de idiomas C.
  2. Existem muitas funções que você não deseja que retornem um valor. O que você vai fazer com "um Successtipo genérico "? De fato, os valores de retorno para indicar sucesso são ainda menos importantes em Java do que em C, porque Java possui exceções para indicar falha e C não.
Mason Wheeler
fonte
8
> "O que você vai fazer com" um tipo genérico de sucesso "afinal?" - ao escrever código genérico, um único tipo de valor (chamado de tipo de unidade btw) é muito útil porque elimina casos especiais de funções que "apenas retornam", mas não retornam nada em particular. Mas quando o Java foi criado, ele tinha um sistema de tipos ainda menos expressivo (sem parâmetros de tipo), portanto não havia muita diferença prática. E agora é tarde demais para mudar. Os idiomas mais modernos evitam o "tipo" nulo (na verdade, nem mesmo é um tipo, apenas um marcador especial para métodos) e usam o tipo de unidade.
precisa saber é o seguinte
9
@SargeBorsch O Voidtipo de Java atua como um tipo de unidade (por exemplo, para genéricos), embora, infelizmente, seja diferente de void(nota lateral: um gatinho fofo morre toda vez que você inventa um novo tipo para corrigir o tipo que você errou na versão anterior). Se alguém não está familiarizado com os tipos de unidade, esta é uma introdução decente: en.wikipedia.org/wiki/Unit_type
Ogre Psalm33
11
@ OgrePsalm33 ele realmente não age como um tipo de Unidade (pelo menos em termos práticos - ainda é necessário escrever "return null;" para retornar do método que possui o tipo de retorno Void, e o compilador AFAIK aloca espaço na pilha para referências do Void , sem aproveitar o fato de que é inútil ler esses valores), é apenas uma convenção usá-lo onde o tipo de unidade "adequado" seria mais apropriado.
precisa saber é o seguinte
3
@immibis porque o tipo de unidade, por definição, deve ter exatamente um valor e o Void não possui valores. Sim, é possível usar null(na verdade, essa é a única opção), mas nulo é uma coisa especial, qualquer valor do tipo de referência em Java pode ser nulo (esse é outro erro no design da linguagem). E, como eu disse anteriormente, se você estiver usando o Voidtipo de retorno em vez da voidmarca, o compilador Java não é seu amigo.
precisa saber é o seguinte
5
O @SargeBorsch Void possui exatamente um valor, que é null. O fato de nullser um membro da maioria dos tipos é irrelevante para saber se Voidé um tipo de unidade.
precisa saber é o seguinte
65

A razão original pela qual a linguagem tem um voidtipo é porque, como em C, os criadores da linguagem não queriam complicar desnecessariamente a sintaxe da linguagem com procedimentos e funções como Pascal.

Essa foi a razão original.

Suas sugestões:

retornando um sinalizador de status

Isso é um não-não. Nós não usamos sinalizadores de status. Se algo der errado, nós o reportamos por meio de exceções.

o objeto que está sendo chamado

O estilo fluente de invocações é um desenvolvimento recente. Muitos dos programadores que estão muito felizes em usar fluentemente hoje em dia e reviram os olhos se tiverem que usar uma interface que não a suporta nem nasceram quando o Java foi criado.

retornando nulo

Isso forçaria você a declarar um método como retornando algo, quando na verdade ele não está retornando nada, portanto seria muito confuso para alguém que está olhando para uma interface tentando descobrir o que faz. As pessoas inevitavelmente inventariam alguma classe inútil que significa "sem valor de retorno", para que todas as funções que não têm nada a retornar possam retornar uma referência nula a essa classe. Isso seria desajeitado. Felizmente, existe uma solução para isso, é chamada void. E essa é a resposta para sua pergunta.

Mike Nakis
fonte
3
Forneça uma fonte para "O motivo original ...". Que eu saiba, a verdadeira razão é porque C foi projetado para ser montador portátil.
Thorbjørn Ravn Andersen
6
@ ThorbjørnRavnAndersen, você está realmente pedindo para fornecer uma fonte para a alegação de que a sintaxe do java é derivada da sintaxe C?
precisa saber é o seguinte
3
@ ThorbjørnRavnAndersen oh, entendi, eu entendi errado. Então, você quer uma fonte para minha afirmação sobre C, não sobre Java. Ok, desculpe, não tenho fonte para isso. Mas acho que é bastante óbvio. (Eu usei a programar em Pascal, em 1987, quando eu mudei para C. Meu Deus, isso é há 30 anos.)
Mike Nakis
6
Isso não é óbvio. O C foi desenvolvido aproximadamente ao mesmo tempo que o pascal por pessoas que provavelmente nem sabiam que ele existia. Por favor, não indique suposições como fatos.
Thorbjørn Ravn Andersen
12
O verdadeiro motivo original foi que, por padrão, as funções C retornaram int, portanto, uma palavra-chave foi adicionada após K&R para indicar 'no return type'.
user207421
20

Alguns métodos, como o System.out.println, não retornam nada útil, mas são chamados exclusivamente para esse efeito colateral. voidé um indicador útil para o compilador e para o leitor de código que nenhum valor útil é retornado.

Retornar em nullvez de voidsignifica que você apenas obteria NullPointerExceptiono momento em que usar esse valor para qualquer coisa. Então você troca um erro de tempo de compilação por um erro de tempo de execução que é muito pior. Além disso, você teria que definir o tipo de retorno como Object, o que seria enganoso e confuso. (E o encadeamento ainda não funcionaria.)

Os códigos de status geralmente são usados ​​para indicar condições de erro, mas o Java possui exceções para essa finalidade.

thisNão é possível retornar de métodos estáticos.

Retornar um Successobjeto genérico não teria nenhum propósito útil.

JacquesB
fonte
11
Concordo totalmente com você, a resposta mais simples e concisa.
precisa saber é o seguinte
11

Uma razão é que pode ser enganoso retornar algo que não seja, digamos null,. Exemplo:

O que deve Arrays.sort(a)retornar?

Se você argumenta que adeve ser retornado para que a chamada possa ser encadeada (que parece ser sua resposta a julgar pela sua pergunta), não fica mais claro se o valor de retorno é uma cópia do objeto original ou o próprio objeto original . Ambos são possíveis. E sim, você pode colocá-lo na documentação, mas é suficientemente ambíguo que você não deve criar a ambiguidade em primeiro lugar.

Por outro lado, se você argumenta que nulldeve ser retornado, isso levanta a questão de quais informações o valor de retorno possivelmente está fornecendo ao chamador e por que o programador deve ser forçado a escrever return nullquando não transmite informações.

E se você retornar algo totalmente absurdo (como o tamanho de a), isso tornará o uso do valor de retorno realmente confuso - pense em quanto mais confuso é dizer em int len = Arrays.sort(a)vez de int len = A.length!

Mehrdad
fonte
11
Com toda a justiça, o programador não precisaria necessariamente escrever return null;- a JVM poderia também exigir que se o controle cair do final de uma função por outro caminho que não seja explícito returnou throwdeclaração, nullseja implicitamente retornado ao chamador. O IIRC C faz isso, exceto que o valor de retorno é indefinido nesse caso (será o valor que estiver no local usado para o valor de retorno no momento).
um CVn
2
A resposta correta para sua pergunta em negrito é "uma matriz classificada".
amigos estão
2
@BryanBoettcher: Você não leu o resto?
Mehrdad
11
@ Michael Kjörling: é uma propriedade fundamental da linguagem Java evitar tais erros, ou seja, não ter “comportamento indefinido” se você esquecer uma returndeclaração. Em vez disso, você recebe um erro do compilador informando sobre seu erro. Inserir um implícito return null;seria melhor que "comportamento indefinido", mas não muito. Isso seria útil apenas quando o tipo de retorno não tem significado, mas de onde o compilador tira a conclusão de que o valor de retorno não tem significado, quando não é void?
Holger
2
@ MichaelKjörling: “Se returnrealmente não agregar valor ...”, bem, como foi dito, não há indicador para o compilador de que um retorno não acrescentaria valor, se não houver um voidtipo. Na verdade, é o contrário, a primeira suposição é que um método que declara um tipo de retorno deseja returnalgo útil desse tipo. E ainda não falamos sobre tipos primitivos, que não têm nullvalor, portanto, qualquer valor padrão injetado pelo compilador entraria em conflito com o intervalo de valores de retorno potencialmente úteis.
Holger
7

Retornar um booleanque é sempre igual ao trueque você sugere, não é apenas inútil (o valor de retorno não traz informações), mas seria realmente enganoso. Na maioria das vezes, é melhor usar tipos de retorno que só confiram tanta informação quanto está realmente disponível - é por isso que em Java você tem booleanpara true/ falseem vez de retornar um intcom 4 bilhões de valores possíveis. Da mesma forma, retornar a booleancom dois valores possíveis, onde existe apenas um valor possível ("sucesso genérico"), seria enganoso.

Também acrescentaria sobrecarga de desempenho desnecessária.

Se um método é usado apenas para seu efeito colateral, não há nada para retornar e o tipo de retorno voidreflete exatamente isso. Erros raros em potencial podem ser sinalizados por meio de exceções.

Uma coisa que poderia ser melhorada seria criar voidum tipo real, como o de Scala Unit. Isso resolveria alguns problemas, como lidar com genéricos.

Michał Kosmulski
fonte
Curiosidade: List.addsempre retorna true, mas é por ser compatível com o contrato mais amplo de Collection.add, portanto, Set.addpode retornar trueou false.
Holger
4

Java é uma linguagem relativamente antiga. E em 1995 (quando foi criado) e logo em seguida os programadores ficaram muito preocupados com a quantidade de tempo que um processo assumiu o controle do processador, bem como com o consumo de memória. Retornar nulo eliminaria alguns ciclos de clock e um pouco de consumo de memória de uma chamada de função, porque você não precisaria colocar o valor de retorno na pilha e não precisaria removê-lo posteriormente.

O código eficiente não devolve algo que você nunca usaria e, portanto, colocar o vazio em algo que tenha um valor de retorno sem sentido é uma prática muito melhor para se praticar do que retornar um valor de sucesso.

The Lazy Coder
fonte
14
programadores que estavam muito preocupados com a velocidade não usariam o Java atualmente, porque as primeiras implementações do Java eram notoriamente lentas. Tão lento que muitas pessoas que atualmente não estão trabalhando com ele ainda têm a impressão de que Java é sinônimo de gordo e lento.
precisa saber é o seguinte
8
O @SargeBorsch me lembra uma velha piada de programação de 20 anos atrás. "TOC Toc." "Quem está aí?" ... ... ... ... pausa muito longa ... ... "Java"
Dan Neely
4
@ DanNeely e algum tempo depois, um carro dirigido por IA bate em uma parede a toda velocidade, sem sequer tentar usar freios. Foi escrito em Java. Então, Java não freia agora! (era um trocadilho em idioma russo, não pode traduzi-lo para Inglês muito bem)
Sarge Borsch
2
@SargeBorsch O que você tem funciona como um trocadilho em inglês, mesmo que tenha perdido algumas nuances na tradução. E traduzir trocadilhos é provavelmente mais difícil que a poesia.
21417 Dan Neely
3
Bons programadores ainda estão preocupados com o desempenho e o consumo de memória, o que torna desnecessariamente pedir às pessoas que devolvam lixo ainda hoje.
precisa saber é o seguinte
2

Eu li argumentos que sugerem que a segunda opção (retorno this) deve ser a abordagem em vez de void. Essa é uma boa idéia, mas a abordagem no estilo do construtor não era popular no momento em que o Java foi criado. Se fosse tão popular naquela época como agora, essa poderia ter sido a abordagem adotada. Retornar nullé uma péssima idéia da OMI. Eu gostaria que nullnem estivesse no idioma.

O problema agora é que, se um método retorna uma instância do seu próprio tipo, não está claro se é um novo objeto ou o mesmo. Freqüentemente não importa (ou não deveria), mas outras vezes importa. Suponho que o idioma possa ser alterado para retornar implicitamente thisaos métodos nulos. O único problema que posso pensar com isso no momento é que se o método fosse alterado posteriormente para retornar um novo objeto do mesmo tipo, não haveria avisos de compilação, mas talvez isso não seja um grande problema. O sistema de tipos atual não se preocupa com esses tipos de alterações agora. Como isso interage com a herança / interfaces precisaria de alguma consideração, mas permitiria que APIs antigas sem um estilo de construtor fossem facilmente chamadas como se fossem.

JimmyJames
fonte
2
@ Cody Bom ponto, isso não se aplicaria a métodos estáticos.
precisa saber é o seguinte
14
@ marisbest2 Qual argumento?
precisa saber é o seguinte
3
Bem, se nullnão fizesse parte do idioma, as pessoas usariam todos os tipos de valores de sentinela que significam "por favor, ignore esse argumento / valor de retorno, ele não contém nenhum valor sensível". O problema com nullnão é a coisa em si, apenas que ela tende a ser usada incorretamente. Aposto que esse problema só seria exagerado se a padronizada nullfosse substituída por uma infinidade de soluções personalizadas, em que cada implementação se comportaria de maneira diferente, como ignorar silenciosamente o uso ou lançar exceções especiais em vez de uma exceção padrão de ponteiro nulo etc.
cmaster
2
Uma substituição óbvia do @cmaster nullé um tipo opcional adequado (como Maybeem Haskell). O problema com nulos em Java é que não há uma maneira sã de decidir imediatamente se um valor é anulável na prática ou não. Isso leva a: programação desnecessariamente defensiva (verificação de nulos onde não faz sentido, aumentando assim a porcentagem de cocô no código) e NPEs acidentais na produção (se esqueceu de verificar onde era necessário). Sim, é parcialmente resolvido por anotações, mas são opcionais, nem sempre marcados, etc. Portanto, eles não o salvarão nos limites com código de terceiros.
precisa saber é o seguinte
2
@SargeBorsch Concordo com isso :-) Minha objeção é contra um idioma sem uma maneira padrão de expressar um "ignore esse valor". nullfunciona bem para isso (fine = simple), mas valores opcionais padronizados com semântica sólida são definitivamente outra opção (fine = safe).
C26 de
2

Outro aspecto:

Java é uma linguagem estática com recursos bastante rígidos. Isso significa que muitas coisas que seriam bastante abertas ou dinâmicas em outros idiomas (c / f Ruby, Lisp etc.) são estritamente determinadas.

Esta é uma decisão geral de design. O "porquê" é difícil de responder (bem, porque os designers da linguagem pensaram que seria bom!). O "para quê" é bastante claro: permite que o compilador detecte muitos erros, o que geralmente é um recurso muito bom para qualquer idioma. Em segundo lugar, torna relativamente fácil raciocinar sobre o idioma. Por exemplo, é relativamente fácil criar provas formais de correção na (subconjuntos) da linguagem Java; como comparação, isso seria praticamente impossível em uma linguagem dinâmica como Ruby et al.

Esse pensamento permeia a linguagem, por exemplo, a declaração forçada de possíveis exceções que um método pode lançar, o tipo separado de interfacevs. classpara evitar herança múltipla ambígua e assim por diante. Para o que é (uma linguagem imperativa estática do mundo real OOP, com forte foco no tratamento de erros em tempo de compilação), essas coisas são realmente bastante elegantes e poderosas. Eles se aproximam das linguagens teóricas (científicas) que são propositalmente criadas apenas para explorar alguns desses problemas do que qualquer outra linguagem do mundo real antes (naquele momento, lembre-se).

Assim. Ter um voidtipo estrito é uma mensagem clara: esse método não retorna nada, ponto final. É o que é. Substituí-lo pela aplicação para sempre retornar algo levaria a um comportamento muito mais dinâmico (como em Ruby, onde cada def tem sempre um valor de retorno explícito ou implícito), o que seria ruim para a provabilidade e o raciocínio; ou inchaço maciço usando algum outro mecanismo aqui.

(E, no entanto, Ruby (por exemplo) lida com isso de maneira diferente, e sua solução ainda é exatamente tão aceitável quanto a de Java, porque possui uma filosofia completamente diferente. Por exemplo, lança a provabilidade e a razoabilidade completamente fora da janela enquanto coloca um grande foco em expressividade extremamente alta do idioma.)

AnoE
fonte