Eu tenho uma classe com um private static final
campo que, infelizmente, preciso alterá-lo em tempo de execução.
Usando reflexão, recebo este erro: java.lang.IllegalAccessException: Can not set static final boolean field
Existe alguma maneira de alterar o valor?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
System.out/in/err
são tão "especiais" que o Java Memory Model precisa fazer uma menção especial a eles. Eles não são exemplos que devem ser seguidos.Respostas:
Assumindo que não
SecurityManager
está impedindo você de fazer isso, você pode usarsetAccessible
para se locomoverprivate
e redefinir o modificador para se livrarfinal
e realmente modificar umprivate static final
campo.Aqui está um exemplo:
Supondo que não
SecurityException
seja lançado, o código acima será impresso"Everything is true"
.O que realmente é feito aqui é o seguinte:
boolean
valores primitivostrue
efalse
inmain
são autoboxed para referenciar o tipoBoolean
"constantes"Boolean.TRUE
eBoolean.FALSE
public static final Boolean.FALSE
referência aoBoolean
referido porBoolean.TRUE
false
é autoboxedBoolean.FALSE
, refere-se ao mesmoBoolean
que o referido porBoolean.TRUE
"false"
agora é"true"
Perguntas relacionadas
static final File.separatorChar
para teste de unidadeInteger
o cache de, mutando aString
, etcRessalvas
Cuidado extremo deve ser tomado sempre que você fizer algo assim. Pode não funcionar porque
SecurityManager
pode estar presente, mas mesmo se não funcionar, dependendo do padrão de uso, pode ou não funcionar.Veja também
private static final boolean
, porque é inlinável como uma constante em tempo de compilação e, portanto, o valor "novo" pode não ser observávelApêndice: Na manipulação bit a bit
Essencialmente,
desativa o bit correspondente a
Modifier.FINAL
partir defield.getModifiers()
.&
é o bit a bit - e, e~
é o bit a bit - complemento.Veja também
Lembre-se de expressões constantes
Ainda não sendo capaz de resolver isso ?, caíram em depressão como eu fiz? Seu código se parece com isso?
Lendo os comentários sobre esta resposta, especialmente a de @Pshemo, lembrou-me que as Expressões Constantes são tratadas de maneira diferente, pelo que será impossível modificá-la. Portanto, você precisará alterar seu código para ficar assim:
se você não é o dono da turma ... eu sinto você!
Para mais detalhes sobre por que esse comportamento foi lido ?
fonte
getDeclaredField()
em vez degetField()
para a classe alvofinal String myConstant = "x";
e falharão: lembre-se de que constantes de tempo de compilação serão incorporadas pelo compilador; assim, quando você escrever um código comoSystem.out.println(myConstant);
ele será compilado,System.out.println("x");
porque o compilador sabe o valor da constante no momento da compilação. Para se livrar desse problema, você precisa inicializar suas constantes no tempo de execução, comofinal String myConstant = new String("x");
. Também no caso de primitivas comofinal int myField = 11
usofinal int myField = new Integer(11);
oufinal Integer myField = 11;
Se o valor atribuído a um
static final boolean
campo for conhecido em tempo de compilação, será uma constante. Os campos primitivos ou deString
tipo podem ser constantes em tempo de compilação. Uma constante será incorporada em qualquer código que faça referência ao campo. Como o campo não é realmente lido em tempo de execução, alterá-lo não terá efeito.A especificação da linguagem Java diz o seguinte:
Aqui está um exemplo:
Se você descompilar
Checker
, verá que, em vez de fazer referênciaFlag.FLAG
, o código simplesmente coloca um valor de 1 (true
) na pilha (instrução # 3).fonte
public static final Boolean FALSE = new Boolean(false)
nãopublic static final boolean FALSE = false
Um pouco de curiosidade do Java Language Specification, capítulo 17, seção 17.5.4 "Campos protegidos contra gravação":
Fonte: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
fonte
Também o integrei com a biblioteca joor
Apenas use
Também corrigi um problema com o
override
qual as soluções anteriores parecem faltar. No entanto, use isso com muito cuidado, somente quando não houver outra boa solução.fonte
Juntamente com a resposta mais bem classificada, você pode usar uma abordagem um pouco mais simples. A
FieldUtils
classe Apache commons já possui um método específico que pode fazer as coisas. Por favor, dê uma olhada noFieldUtils.removeFinalModifier
método. Você deve especificar a instância do campo de destino e o sinalizador forçador de acessibilidade (se você jogar com campos não públicos). Mais informações você encontra aqui .fonte
java.lang.UnsupportedOperationException: In java 12+ final cannot be removed.
No caso de presença de um Security Manager, pode-se fazer uso de
AccessController.doPrivileged
Tomando o mesmo exemplo da resposta aceita acima:
Na expressão lambda,,
AccessController.doPrivileged
pode ser simplificado para:fonte
A resposta aceita funcionou para mim até a implantação no JDK 1.8u91. Então percebi que ele falhou na
field.set(null, newValue);
linha quando li o valor via reflexão antes de chamar osetFinalStatic
método.Provavelmente, a leitura causou uma configuração diferente dos internos de reflexão do Java (ou seja,
sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
no caso de falha em vez desun.reflect.UnsafeStaticObjectFieldAccessorImpl
no caso de sucesso), mas eu não o elaborei mais.Como eu precisava definir temporariamente o novo valor com base no valor antigo e, posteriormente, retornar o valor antigo, alterei um pouco a assinatura para fornecer a função de computação externamente e também retornar o valor antigo:
No entanto, para casos gerais, isso não seria suficiente.
fonte
Mesmo sendo
final
um campo, pode ser modificado fora do inicializador estático e (pelo menos JVM HotSpot) executará o bytecode perfeitamente.O problema é que o compilador Java não permite isso, mas isso pode ser facilmente ignorado usando
objectweb.asm
. Aqui está um arquivo de classe perfeitamente válido que passa na verificação de código de código e é carregado e inicializado com êxito no JVM HotSpot OpenJDK12:Em Java, a classe tem a seguinte aparência:
que não pode ser compilado
javac
, mas pode ser carregado e executado pela JVM.O JVM HotSpot possui tratamento especial para essas classes, no sentido de impedir que essas "constantes" participem de dobras constantes. Essa verificação é feita na fase de reescrita de bytecode da inicialização da classe :
A única restrição que a JVM HotSpot verifica é que o
final
campo não deve ser modificado fora da classe em que ofinal
campo está declarado.fonte
Só vi essa pergunta em uma das perguntas da entrevista, se possível alterar a variável final com reflexão ou em tempo de execução. Fiquei realmente interessado, para que eu me tornasse:
Alguma classe simples com a variável String final. Portanto, na classe principal import java.lang.reflect.Field;
A saída será a seguinte:
De acordo com a documentação https://docs.oracle.com/javase/tutorial/reflect/member/fieldValues.html
fonte
static
campo final, portanto esse código não funciona.setAccessible(true)
funciona apenas para definir campos de instância final.Se o seu campo é simplesmente privado, você pode fazer o seguinte:
e throw / handle NoSuchFieldException
fonte
O ponto principal de um
final
campo é que ele não pode ser reatribuído depois de definido. A JVM usa essa garantia para manter a consistência em vários locais (por exemplo, classes internas que referenciam variáveis externas). Então não. Ser capaz de fazer isso quebraria a JVM!A solução é não declarar
final
em primeiro lugar.fonte
final
tem um papel especial na execução multithread - a alteração definal
valores também quebraria o modelo de memória Java.final
não deve ser declaradostatic
.