Por que temos o comprimento de uma matriz como um atributo array.length
, e para String temos um método str.length()
,?
Existe algum motivo?
Por que temos o comprimento de uma matriz como um atributo array.length
, e para String temos um método str.length()
,?
Existe algum motivo?
Respostas:
Deixe-me primeiro destacar três maneiras diferentes para fins semelhantes.
length
- matrizes ( int[]
, double[]
, String[]
) - para saber o comprimento das matrizes
length()
- Cadeia objecto relacionado ( String
, StringBuilder
, etc.) - a saber o comprimento da corda
size()
- Objeto Coleção ( ArrayList
, Set
, etc.) - para saber o tamanho da coleção
Agora esqueça de length()
considerar apenas length
e size()
.
length
não é um método, portanto, faz todo o sentido que não funcione em objetos. Ele só funciona em matrizes.
size()
seu nome o descreve melhor e por ser um método, ele será usado no caso daqueles objetos que trabalham com coleção (frameworks de coleção) como eu disse lá em cima.
Agora vamos para length()
:
String não é um array primitivo (então não podemos usar .length
) e também não é uma Collection (então não podemos usar .size()
), por isso também precisamos de um diferente que é length()
(mantenha as diferenças e atenda ao propósito).
Como resposta a por quê?
Acho que é útil, fácil de lembrar e usar e amigável.
Um pouco simplificado, você pode pensar nisso como arrays sendo um caso especial e não classes comuns (um pouco como primitivas, mas não). String e todas as coleções são classes, daí os métodos para obter tamanho, comprimento ou coisas semelhantes.
Eu acho que a razão na época do design era o desempenho. Se eles o criaram hoje, provavelmente teriam criado algo como classes de coleção baseadas em array.
Se alguém estiver interessado, aqui está um pequeno trecho de código para ilustrar a diferença entre os dois no código gerado, primeiro a fonte:
public class LengthTest {
public static void main(String[] args) {
int[] array = {12,1,4};
String string = "Hoo";
System.out.println(array.length);
System.out.println(string.length());
}
}
Cortar uma parte não tão importante do código de bytes, rodando javap -c
na classe resulta no seguinte para as duas últimas linhas:
20: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: arraylength
25: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
28: getstatic #3; //Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual #5; //Method java/lang/String.length:()I
35: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
No primeiro caso (20-25), o código apenas pergunta ao JVM o tamanho do array (em JNI, isso seria uma chamada para GetArrayLength ()), enquanto no caso de String (28-35) ele precisa fazer um chamada de método para obter o comprimento.
Em meados dos anos 1990, sem bons JITs e outras coisas, teria matado totalmente o desempenho ter apenas o java.util.Vector (ou algo semelhante) e não uma construção de linguagem que realmente não se comportasse como uma classe, mas fosse rápida. Eles poderiam, é claro, ter mascarado a propriedade como uma chamada de método e manipulado no compilador, mas acho que seria ainda mais confuso ter um método em algo que não é uma classe real.
Vector
quando a linguagem Java não tivesse suporte a array?
Considerar:
int[] myArray = new int[10];
String myString = "hello world!";
List<int> myList = new ArrayList<int>();
myArray.length // Gives the length of the array
myString.length() // Gives the length of the string
myList.size() // Gives the length of the list
É muito provável que strings e arrays tenham sido projetados em momentos diferentes e, portanto, acabaram usando convenções diferentes. Uma justificativa é que, como Strings usam arrays internamente, um método,, length()
foi usado para evitar a duplicação das mesmas informações. Outra é que usar um método length()
ajuda a enfatizar a imutabilidade das strings, embora o tamanho de uma matriz também seja imutável.
No final das contas, essa é apenas uma inconsistência que evoluiu e que definitivamente seria corrigida se a linguagem fosse redesenhada do zero. Até onde eu sei, nenhuma outra linguagem (C #, Python, Scala, etc.) faz a mesma coisa, então essa provavelmente é apenas uma pequena falha que acabou fazendo parte da linguagem.
Você obterá um erro se usar o errado de qualquer maneira.
Em Java, um Array armazena seu comprimento separadamente da estrutura que realmente contém os dados. Ao criar um Array, você especifica seu comprimento, e isso se torna um atributo de definição do Array. Não importa o que você faça com um Array de comprimento N (alterar valores, anular coisas, etc.), sempre será um Array de comprimento N.
O comprimento de uma String é acidental; não é um atributo da String, mas um subproduto. Embora Java Strings sejam de fato imutáveis, se fosse possível alterar seu conteúdo, você poderia alterar seu comprimento. Eliminar o último caractere (se fosse possível) diminuiria o comprimento.
Eu entendo que esta é uma boa distinção e posso ser rejeitado a favor, mas é verdade. Se eu fizer um Array de comprimento 4, esse comprimento de quatro é uma característica definidora do Array, e é verdadeiro independentemente do que é mantido dentro. Se eu fizer uma String que contém "cachorros", essa String terá um comprimento 4 porque contém quatro caracteres.
Eu vejo isso como uma justificativa para fazer um com um atributo e outro com um método. Na verdade, pode ser apenas uma inconsistência não intencional, mas sempre fez sentido para mim e sempre foi assim que pensei sobre isso.
Aprendi que, para arrays, o comprimento não é recuperado por meio de um método devido ao seguinte medo: os programadores apenas atribuem o comprimento a uma variável local antes de entrar em um loop (pense em um loop for em que a condicional usa o comprimento do array.) O programador supostamente faria isso para diminuir as chamadas de função (e assim melhorar o desempenho). O problema é que o comprimento pode mudar durante o loop, mas a variável não.
Sempre que uma matriz é criada, seu tamanho é especificado. Portanto, o comprimento pode ser considerado um atributo de construção. Para String, é essencialmente uma matriz char. O comprimento é uma propriedade da matriz char. Não há necessidade de colocar o comprimento como um campo, porque nem tudo precisa desse campo. http://www.programcreek.com/2013/11/start-from-length-length-in-java/
Eu só quero acrescentar algumas observações à ótima resposta de Fredrik .
A especificação da linguagem Java na Seção 4.3.1 declara
Um objeto é uma instância de classe ou um array .
Portanto, array tem, de fato, um papel muito especial em Java. Eu me pergunto por quê.
Pode-se argumentar que o array de implementação atual é / foi importante para um melhor desempenho. Mas então é uma estrutura interna, que não deve ser exposta.
Eles poderiam, é claro, ter mascarado a propriedade como uma chamada de método e manipulado no compilador, mas acho que teria sido ainda mais confuso ter um método em algo que não é uma classe real.
Eu concordo com Fredrik, que uma otimização do compilador inteligente teria sido a melhor escolha. Isso também resolveria o problema, que mesmo se você usar uma propriedade para arrays, você não resolveu o problema para strings e outros tipos de coleção (imutáveis), porque, por exemplo, string
é baseado em um char
array como você pode ver na definição de classe de String
:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // ...
E eu não concordo com isso seria ainda mais confuso, porque array herda todos os métodos dejava.lang.Object
.
Como engenheiro, realmente não gosto da resposta "Porque sempre foi assim". e gostaria que houvesse uma resposta melhor. Mas, neste caso, parece que sim.
tl; dr
Na minha opinião, é uma falha de design do Java e não deveria ter sido implementado dessa forma.