Por que o Java String não possui métodos estáticos de manipulação de strings?

17

Por que os designers de Java não criaram versões estáticas dos métodos de manipulação de string na java.lang.Stringclasse? Os métodos a seguir são os que me refiro, mas a pergunta também pode ser estendida a outros métodos não estáticos da classe.

concat(String)                        substring(int, int)
replace(char, char)                   toLowerCase()
replace(CharSequence, CharSequence)   toLowerCase(Locale)
replaceAll(String, String)            toString()
replaceFirst(String, String)          toUpperCase()
split(String)                         toUpperCase(Locale)
split(String, int)                    trim()
substring(int)

Ter apenas versões não estáticas desses métodos força a verificação nula explícita em qualquer lugar em que esse método precise ser chamado. Por exemplo, simplesmente chamar example = example.trim()levaria a NullPointerException if String example = null. Portanto, o programador deve fazer a seguinte verificação nula padrão:

if (example != null)
    example = example.trim();
// OR:
example = (example==null) ? null : example.trim();
example = (example==null) ? null : example.substring(5);

Eu imagino que teria sido muito mais conveniente se Stringtivesse versões estáticas desses métodos (talvez até exclusivamente ), que usariam a string de entrada como o primeiro argumento:

example = String.trim(example);
example = String.replace(example, 'a', 'b');
example = String.substring(example, 5);

Isso levaria a um código mais limpo, escrito por programadores, que cuidaria automaticamente de casos nulos simplesmente retornando nulo, em vez de forçar os programadores a lidar explicitamente com casos nulos. O retorno de null faz sentido para mim, pois a manipulação de uma string nula deve resultar em uma string nula , não em erro.

Por que os designers de Java não pensaram nisso quando criaram a Stringclasse em Java 1 ou Java 2, ou mesmo adicionaram essa funcionalidade em uma versão Java posterior?

ADTC
fonte
17
Eu sugeriria substituir "Por que os designers de Java não pensaram em X" por "Por que os designers de Java decidiram contra o X". Dê a eles o crédito básico de não serem cegos para a opção.
Avner Shahar-Kashtan
5
nullé um estado excepcional e deve ser tratado explicitamente.
CodesInChaos
2
@KonradMorawski: Pessoalmente, considero um mau uso grosseiro dos métodos de extensão. Se você tem um valor nulo, o que você está tentando chamar com métodos, simplesmente confunde todo mundo que lê esse código. É uma reimplementação ruim de um Maybe<T>tipo, eu acho?
Phoshi 12/12
1
@Phoshi é preciso ter em mente que os métodos de extensão NÃO são métodos reais, são apenas açúcar de sintaxe. Mas concordo que é controverso. Eu gostaria que o C # / Java oferecesse algum tipo de segurança nula sintática integrada, como - digamos - Kotlin. string name = employee?.personalData?.name. Apenas como um atalho para todos esses repetitivos if (employee != null). Pergunta relacionada ao SO: stackoverflow.com/questions/1196031/…
Konrad Morawski
2
@ADTC Erm, você percebe que um programa não pode prever com antecedência que um valor é nulo, certo? - Você deve definir os contratos entre as interfaces de uma forma que você pode ter a certeza se um valor é permitido ser nulo ou não. A verificação nula em qualquer lugar significa que você tem um problema de design do programa.
Uooo

Respostas:

30

O retorno de null faz sentido para mim, pois a manipulação de uma string nula deve resultar em uma string nula, não em erro

Bem, essa é a sua opinião. Outros podem argumentar que as operações String em um objeto nulo, que não contém uma String, não fazem sentido e, portanto, devem lançar uma Exceção

Por que "designers de Java" fizeram ou não algo é difícil de responder.

Já existem bibliotecas por aí que podem executar operações de String com segurança nula, por exemplo, StringUtils do Apache Commons. Você pode usar essas bibliotecas ou similares, se precisar delas.


Adição com base nos seus comentários

Lendo os comentários, parece que o verdadeiro problema que você enfrenta é que você precisa verificar nulltodo o seu código. Isso pode ser um indicador de um mau design do programa sem contratos claramente definidos entre interfaces. Você pode querer ler Evitando instruções "! = Null" em Java?

Uooo
fonte
1
Obrigado pelo seu feedback! Acho que a verdadeira questão é: por que precisamos depender de uma biblioteca externa ou de aulas caseiras quando um recurso tão básico pode fazer parte do Java padrão?
ADTC
4
O @ADTC coloca a questão de outra maneira: por que os "designers de Java" devem incluir algo na linguagem, se já existem muitas boas bibliotecas por aí, bem testadas e documentadas? Como definir um "recurso básico" é baseado em opiniões. Quase todos os projetos não precisam de alguma biblioteca externa para "Funcionalidade básica" (teste de unidade, zombaria, data e hora, ...)? Não é grande coisa, é?
Uooo
Uma maneira de lidar com nulos é usar o Guava's Optional- code.google.com/p/guava-libraries/wiki/…
Konrad Morawski
10

Na IMO, toda a abordagem "se arg é nulo retorna nulo" para escrever métodos desnecessariamente aumenta a complexidade ciclomática do seu programa.

As bibliotecas Java anteriores usavam a abordagem de retorno nulo, mas os caras do JDK sempre se protegiam contra o nulo, com exceções.

Com o tempo, acho que a última abordagem saiu como a clara vencedora.

Por quê? Como os campos nulos quebram muitos princípios. Você simplesmente não pode tratá-los como membros de uma classe polimórfica. Isso significa que você sempre deve codificar casos especiais para nulo e, portanto, sua complexidade ciclomática aumenta quando você assume que as coisas podem ser nulas.

Para resolver seus problemas, considere usar o padrão de objeto nulo em vez de confiar em campos nulos.

Alexander Torstling
fonte
2
Mas Stringnão é polimórfico, não? E como você usa o padrão de objeto nulo para um tipo já existente como String?
svick
String não é polimórfica, não. Mas, mesmo assim, você gostaria de saber que pode chamar métodos de instância em qualquer referência de string que tenha. Isso funciona para todas as referências não nulas. A sequência já possui um objeto nulo: a sequência vazia. Assim como as listas têm a lista vazia. Então, pergunte-se por que a sequência vazia não é suficiente no seu caso. Eu acho que você usa uma string nula para sinalizar algum caso especial. Pergunte a si mesmo se você pode usar uma bandeira separada para esse caso especial.
Alexander Torstling
@AlexanderTorstling: um campo do tipo intpadrão é zero em vez de null. Conceitualmente, deve ser possível ter um stringtipo cujo valor padrão seja uma string vazia em vez de null[esse tipo pode ser semanticamente o Stringque inté necessário Integer]. O fato de um não-vazio stringconter internamente uma referência a um objeto de heap seria um detalhe de implementação; não há razão para que ele não se comporte semanticamente como um primitivo.
Supercat 23/12
@ supercat: Concordo. Eu acho que teria sido melhor proibir valores nulos para refefences, a menos que explicitamente ativado. Campos não nulos e finais padrão. Muitos tipos já tem objetos de comportamento nulos
Alexander Torstling
@AlexanderTorstling: Ter locais de armazenamento de todos os tipos padrão como all-bytes-zero e fornecer que tipos de estrutura possam ser atribuídos via cópia de todos os bytes simplifica bastante a estrutura, mas praticamente implica que tipos com semântica de referência mutável devem ter como padrão nulo. Teria sido útil, no entanto, ter suporte de estrutura para tipos de valor que encapsulariam uma única referência e "encaixariam" como - e poderiam unbox - desse tipo de referência .
Supercat 25/13 /
3

Sem dizer que esse é o motivo, mas, exceto para toString, todos esses métodos são facilmente encapsulados em uma classe auxiliar estática, enquanto o inverso não é verdadeiro.

Ou seja, se você deseja Stings.toUpper (entrada de string), o código para isso é fácil de escrever, mas se você quer instance.toUpper e tudo o que você tem é String.toUpper, bem, isso é impossível ... a menos que eles introduzam métodos de extensão, o que provavelmente nem foi considerado na época.

jmoreno
fonte
Bom ponto, embora mais um comentário do que uma resposta. Mas, mesmo considerando isso, por que precisamos depender de uma biblioteca externa ou de classes criadas em casa, quando um recurso tão básico pode fazer parte do Java padrão?
ADTC
2
A partir de agora, você tem String.format(estático), mas não pode "something %s".format(id).
111313 Konrad Morawski
@adtc: porque não é uma característica básica da linguagem fazê-lo através de uma classe estática. A partir dessa premissa, há muitas razões para não fazer isso - código / inchaço desnecessário, funcionalidade duplicada, custo de desenvolvimento / manutenção.
jmoreno
-2

Em java, essa string é um objeto. Essa é uma escolha de design.

As referências a objetos podem ser nulas e nulas se não houver um objeto atribuído a ela. Se você chamar esse objeto, uma NullPointerException será lançada. Isso é oo-comportamento padrão.

Os designers de Java escolheram ter strings como objeto e ter esses métodos lançando exceções de ponteiros nulos é o que você obtém se quiser que os strings sejam objetos.

Por que os designers de Java não pensaram nisso quando criaram a classe String em Java 1 ou Java 2, ou mesmo adicionaram essa funcionalidade em uma versão Java posterior?

Eles provavelmente pensaram sobre isso e optaram por incorporar oo-comportamento.

Pieter B
fonte
Posso saber, como é a criação de métodos estáticos para cuidar de casos nulos e não do comportamento de OO ? Para ser claro, não estou dizendo que sim, mas estou tentando entender por que você diz que a decisão de design é incorporar o comportamento do OO.
ADTC
Métodos estáticos são mais parecidos com funções. E, para o uso que você deseja, elas nem deveriam fazer parte da classe string, e sim parte de alguma classe utilitária. Assim como as funções da aula de matemática.
Pieter B
4
Só porque strings são objetos não significa que a classe String não possa ter métodos estáticos. E, de fato, já tem alguns, por exemplo. String.format. (Mesmo em C #, a propósito). Portanto, se essa é uma opção de design, não pode vir apenas do fato de que as strings são objetos e não são aplicadas de forma consistente.
Konrad Morawski
@PieterB Eu não reclamaria se pelo menos esses métodos fossem fornecidos em uma Stringsclasse de utilitário, como a Arraysclasse de utilitário ( embora eu entenda que ele foi criado por pura necessidade, porque as matrizes são uma espécie de cruzamento estranho entre primitivos e objetos: ) ) .. desde que fizesse parte do Java padrão e não exigisse dependências.
ADTC