Alguém saberia me explicar qual a utilidade dos sufixos e prefixos que fazem a coerção de tipos sobre constantes?
Sei o que eles fazem, mas não entendo por que eles são necessários, visto que quando atribuímos uma constante a uma variável
o tipo dessa constante é automaticamente transformado para o tipo da variável que a recebe.
O que você está chamando de “constante”? Seria os literais?
Por que isso é atribuir uma constante a uma variável:
private static final float CONSTANTE = 4; //Inicialização de constante com literal
int valor = (int) CONSTANTE; //Atribuição de constante a uma variável
E, nesse caso, a variável valor não troca de tipo, pois ela é inteira, e continua inteira.
No caso de cima, o cast não é necessário, pois o literal “cabe” dentro de um float e o compilador é esperto o suficiente para saber disso.
Poderia dar um exemplo do caso que você está falando?
O que você chama de “constante” é o “literal”, certo?
Os sufixos existentes servem para especificar o tipo com exatidão. São:
F - indica que o literal numérico é do tipo “float” (por default, um literal numérico inteiro é do tipo “int” e um de ponto flutuante é do tipo “double”)
L - indica que o literal numérico é do tipo “long”. Exemplo: 0b1010000101000101101000010100010110100001010001011010000101000101L
Note que em Fortran, uma linguagem remotamente aparentada com o Java, existe a letra “D” (que indica um tipo “double precision”, que é correspondente ao “double” do Java) e que é usada na notação científica para substituir a letra E: - por exemplo, 3.14D00 indica o número 3.14 como um double.
Como em C e em Java o tipo default para constantes é o double, não o float, então optou-se por especificar as constantes de ponto flutuante de 32 bits - float - com a letra F, porque elas são muito, muito menos frequentes.
Os prefixos existentes servem para indicar qual é a base usada para representar os números. São:
0x - indica que o literal numérico inteiro está em hexadecimal
0 - indica que o literal numérico inteiro está em octal
0x…p… - indica que o literal numérico de ponto flutuante está em hexadecimal, e que está em notação científica (o que vem depois do “p” , em decimal, é o expoente (supondo que a base seja 2) ). Por exemplo, 0x1.fffffffffffffp1023 ou 0x0.0000000000001P-1022 ( veja https://blogs.oracle.com/darcy/entry/hexadecimal_floating_point_literals ).
0b - indica que o literal numérico inteiro está em binário. http://docs.oracle.com/javase/7/docs/technotes/guides/language/binary-literals.html
Por que é que eles existem, já que você também tem os “casts”? (como você deve saber, cast é sempre representado como (tipo) e somente nesse caso é que fazem um conversão)
A ideia é a seguinte: sempre que possível, os sufixos e prefixos devem ser suficientes para especificar uma constante.
Só em caso de ambiguidade é necessário usar um cast explícito
(Acho que está faltando, na verdade, um sufixo para indicar que uma constante é um byte. )
Vou indicar quando é necessário especificar com precisão o tipo.
Lembram-se do autoboxing?
Ele não é suficientemente esperto (de propósito, para evitar comportamento muito imprevisível) para converter uma constante int em um wrapper java.lang.Long; você precisa especificar já na constante que ela é um long - ou usando a letra L, ou então
Outro motivo é quando a constante é suficientemente grande - você precisa usar uma letra L para indicar que a constante é realmente grande de propósito. Por exemplo, 123456789012345 tem de ser especificada como 123456789012345L, já que esse número não cabe em um int.
De modo geral, aqui está o princípio do Java de mínima surpresa - mesmo que para isso, você precise ser meio burocrático e um monte de coisas acabe não ficando automáticas de propósito.
Lembram-se que os carros modernos não exatamente “estacionam sozinhos” - você precisa controlar o pedal do acelerador, e ficar com o pé no freio pronto para brecar o carro em caso de emergência?
Então - é mais ou menos isso.
O controle do acelerador também poderia ser feito pelo computador que estaciona o seu carro naquela vaga apertada, mas isso foi deixado nas mãos do motorista, até porque pode haver algum problema do tipo “uma criança resolveu passar no meio do seu carro, achando que dava tempo, e você tem de parar o carro você mesmo”.
ViniGodoy,
entanglement,
Sim, quando eu disse constantes queria dizer literais.
O entanglement entendeu parte da minha dúvida. Queria entender qual a utilidade dos sufixos L e F, mas os prefixos dos quais eu falava não são os que especificam a base numérica mas sim os que também especificam o tipo do literal, como o mesmo L, que quando aparece antes de um caractere literal define que o mesmo é do tipo wchar_t (na linguagem C++) ou o u8 que aparece antes de strings literais. Não sei se todos esses sufixos e prefixos existem também na linguagem Java, mas acredito que sim.
Acho que entendi a explicação do entanglement sobre a utilidade do sufixo L. Se for criado um literal inteiro que não caiba em um int, perde-se informação se não for usado o sufixo L. Mas e no caso do sufixo F sobre literais de ponto flutuante? Estou forçando um literal a ser de um tipo que ocupa o mesmo ou menor espaço em memória. Não vejo por que usar o sufixo.
Obrigado pelas respostas!
“Acho que sim” não é conhecimento. Para saber exatamente o que está especificado, veja:
L (para wchar_t) ou U, u8, u são específicos do C++: http://stackoverflow.com/questions/7636797/unicode-string-literals
O “F” foi coisa importada do C - não esqueça que o James Gosling tentou criar uma linguagem a mais parecida possível com o C++, mas muito menos complexa.
Nisso ele copiou o “F” em constantes de ponto flutuante float (32 bits). (Em alguns compiladores C/C++ existe também o “L” para indicar que a constante é “long double”, ou seja, 80 bits).
Portanto, há muitas coisas que existem em Java que na verdade são vestígios evolucionários.
Um deles, que odeio até hoje, é que java.util.Date, java.util.Calendar etc. indicam que o mês Janeiro é 0, não 1. Isso é coisa do C.
Além dos casos citados pelo entanglement, acho que a principal utilidade real de literais sufixados é atribuir uma conta a uma constante:
float MEIO = 1 / 2f; //Sem o f seria uma conta entre inteiros
No mais, o compilador costuma a ser esperto o suficiente para saber se um literal cabe ou não numa variável.
entanglement,
Não sou um grande fã da linguagem Java (sou muito mais interessado em C++) e por isso tenho muito pouco conhecimento da linguagem.
Mas fiz a pergunta aqui neste fórum porque sei que é o melhor do Brasil sobre a plataforma Java, e também porque sei que o Java herdou
muita coisa, principalmente a sintaxe, do C++.
Mas, enfim, continuo com a dúvida sobre o sufixo F em literais de ponto flutuante.
ViniGodoy,
Acho que um dos motivos para a existência do sufixo F pode ser o exemplo que você deu, mas a adição de um .0 em um dos operandos já resolveria o problema.
Vou explicar por que é que existe o sufixo F em C.
Em C, que é uma linguagem muito antiga e permite declarar métodos externos sem checagem de parâmetros, você pode ter algo como:
extern int f();
onde f pode estar declarada como:
int f (float x) {
}
(Se você for realmente antigo, vai ver código em C em que o método acima é declarado como:
int f (x)
float x;
{
}
O problema é que se você tentar chamar o método “f” com a seguinte sintaxe:
f (10.0);
você estará passando 8 bytes em vez de 4 no stack dos parâmetros do método f. Isso vai fazer com que haja um problema muito difícil de diagnosticar .
Como a linguagem é antiga, e os programadores C são notórios por não gostar de escrever mais que o necessário, em vez de você escrever explicitamente:
f ((float) 10.0);
o padrão é usar:
f (10.0f);
Note que há limites ao que o C pode fazer em termos de casts implícitos. Não abuse das coisas “implícitas” - você pode ter problemas mais tarde.
Uma coisa que eu não gosto em C é quando as pessoas escrevem:
if (strcmp (str1, str2)) {
Eu prefiro que se escreva:
if (strcmp (str1, str2) != 0) {
que é exatamente o mesmo código e explicita a intenção de escrever:
if (str1 != str2) {
Como eu disse, o C é um parente distante do Fortran.
Só que em Fortran, a constante de ponto flutuante padrão é de 32 bits (o float do C e do Java), e a letra D (por exemplo, 10.3D+12) especifica uma constante de 64 bits (“DOUBLE PRECISION” em Fortran).
Em C, o default foi invertido (tudo é “double”, exceto se você explicitar que não é) e é por isso que existe o F para indicar que a constante é de 32 bits.
EDIT - realmente a constante em Fortran é “10.3D+12”, não “10.3E+12” (que é do tipo de ponto flutuante de 32 bits - acho que isso é chamado “REAL” ou “Single Precision”.
Tecnicamente, o C faz parte das linguagens parentes da linguagem Algol (por exemplo, a Pascal ou o Modula-3, e o Ada).
Mas em vez de usar as palavras-chave “begin/end”, C usa chaves, mas quanto ao resto, é uma das linguagens do tipo Algol.
Não se você quiser que o resultado seja float, e não double.
De modo geral:
Muito do que existe no Java é herança de alguma outra linguagem (não há nada de mal nisso). Às vezes foi uma herança mal-implementada (intencional ou acidentalmente).
Por exemplo, a classe java.util.Formatter (disponível a partir da versão 5 do Java) é uma forma organizada de implementar um recurso muito útil do C (nem do C++ é …) que é a saída formatada, estilo printf. (A classe java.util.Scanner serve para implementar a não tão útil scanf).
O fato de Calendar.JANUARY ser 0, não 1 como qualquer pessoa acharia que deveria ser, é herança do C.
Há no entanto algumas coisas que são sutilezas. Por exemplo:
O fato de byte ser um tipo com sinal e equivaler ao “char” do C foi um engano. Eu acharia melhor que ele fosse equivalente ao unsigned char do C.
O tipo char do Java é equivalente ao tipo “unsigned short” do C (não exatamente ao wchar_t do C, porque esse tipo é dependente de implementação e o char do Java tem sempre 16 bits, sem sinal) .
OK! Valeu!