private static final
불행히도 런타임에 변경 해야하는 필드 가있는 클래스가 있습니다.
리플렉션을 사용하면이 오류가 발생합니다. java.lang.IllegalAccessException: Can not set static final boolean field
값을 변경하는 방법이 있습니까?
Field hack = WarpTransform2D.class.getDeclaredField("USE_HACK");
hack.setAccessible(true);
hack.set(null, true);
답변
no SecurityManager
로 인해이 작업을 수행 할 수 없다고 가정하면 수정자를 setAccessible
해결 private
하고 재설정하여 final
실제로 private static final
필드를 수정하는 데 사용할 수 있습니다.
예를 들면 다음과 같습니다.
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
아니오를 가정하면 SecurityException
위의 코드가 인쇄 "Everything is true"
됩니다.
실제로 여기서 수행되는 작업은 다음과 같습니다.
- 프리미티브
boolean
값true
과false
의가main
참조 형식으로되어 오토 박싱Boolean
“정수”Boolean.TRUE
및Boolean.FALSE
- 리플렉션은
public static final Boolean.FALSE
참조하는 참조 를 변경하는 데 사용Boolean
됩니다.Boolean.TRUE
- 결과적으로 이후
false
에Boolean.FALSE
, 동일한 지칭Boolean
하게 참조됩니다.Boolean.TRUE
"false"
지금은 모든 것이"true"
관련 질문
- 리플렉션을 사용하여
static final File.separatorChar
단위 테스트 변경 - setAccessible을 “합법적 인”용도로만 제한하는 방법은 무엇입니까?
Integer
의 캐시 를 엉망으로 만들고, 돌연변이하는String
등 의 예가 있습니다.
경고
이와 같은 일을 할 때마다 매우주의해야합니다. SecurityManager
존재 하기 때문에 작동하지 않을 수 있지만 사용 패턴에 따라 작동하지 않거나 작동하지 않을 수 있습니다.
역 직렬화와 같은 일부 경우 시스템은
final
시공 후 객체 의 필드 를 변경해야합니다 .final
필드는 리플렉션 및 기타 구현 종속 수단을 통해 변경 될 수 있습니다. 이것이 합리적인 의미론을 갖는 유일한 패턴은 객체가 구성된 다음final
객체 의 필드가 업데이트되는 패턴입니다. 객체 필드에 대한final
모든 업데이트final
가 완료 될 때까지 객체를 다른 스레드에 표시하거나 필드를 읽지 않아야합니다 .final
필드 고정은final
필드가 설정된 생성자의 끝 과final
리플렉션 또는 기타 특수 메커니즘을 통해 필드가 수정 될 때마다 발생 합니다.그럼에도 불구하고 많은 합병증이 있습니다. 경우
final
필드가 필드 선언에서 컴파일 타임 상수로 초기화되면,로 변경final
final
로 컴파일 시간에 대체 되므로 필드 이 관찰되지 않을 수 있습니다 .또 다른 문제는 사양이
final
필드를 적극적으로 최적화 할 수 있다는 것 입니다. 스레드 내final
에서 생성자에서 발생하지 않는 최종 필드를 수정 하여 필드 읽기를 재정렬 할 수 있습니다.
또한보십시오
- JLS 15.28 상수 표현
private static final boolean
컴파일 타임 상수로 인라인 할 수 없으므로 “새로운”값을 관찰 할 수 없기 때문에이 기술이 프리미티브와 함께 작동 하지는 않습니다.
부록 : 비트 조작
본질적으로
field.getModifiers() & ~Modifier.FINAL
Modifier.FINAL
from에 해당하는 비트를 끕니다 field.getModifiers()
. &
비트 단위이며~
비트 단위입니다.
또한보십시오
상수 표현식 기억
아직도이 문제를 해결할 수 없습니까? 제가했던 것처럼 우울증에 빠졌습니까? 코드가 다음과 같이 보입니까?
public class A {
private final String myVar = "Some Value";
}
이 답변, @Pshemo에 의해 특별히 하나에 주석을 읽고, 그 생각 나게 상수 표현식 이 될 수 있도록 서로 다른 처리 불가능 을 수정할 수 있습니다. 따라서 다음과 같이 코드를 변경해야합니다.
public class A {
private final String myVar;
private A() {
myVar = "Some Value";
}
}
당신이 수업의 주인이 아니라면 …
이 동작은 이유에 대한 자세한 내용은 이 글을 읽을 ?
답변
값이 static final boolean
필드에 이 컴파일 타임에 알려진 경우 상수입니다. 프리미티브 또는 String
유형의 필드는
컴파일 타임 상수 일 수 있습니다. 필드를 참조하는 모든 코드에서 상수가 인라인됩니다. 필드는 실제로 런타임에 읽지 않으므로 필드를 변경해도 아무런 영향을 미치지 않습니다.
그만큼 Java 언어 사양 이 말한다 :
필드가 상수 변수 (§4.12.4) 인 경우 키워드 final을 삭제하거나 값을 변경해도 기존 바이너리가 실행되지 않아 기존 바이너리와의 호환성이 손상되지는 않지만 사용법에 대한 새로운 값은 표시되지 않습니다 다시 컴파일하지 않으면 필드의 사용법 자체가 컴파일 타임 상수 표현식이 아닌 경우에도 마찬가지입니다 (§15.28)
예를 들면 다음과 같습니다.
class Flag {
static final boolean FLAG = true;
}
class Checker {
public static void main(String... argv) {
System.out.println(Flag.FLAG);
}
}
디 컴파일 Checker
하면을 참조하는 대신 Flag.FLAG
코드가 단순히 true
스택 에 1 ( ) 값을 푸시 한다는 것을 알 수 있습니다 (지침 # 3).
0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokevirtual #3; //Method java/io/PrintStream.println:(Z)V
7: return
답변
Java 언어 사양 17 장 17.5.4 “쓰기 방지 필드”에 대한 약간의 호기심 :
일반적으로 final 필드와 static 필드는 수정되지 않을 수 있습니다. 그러나 System.in, System.out 및 System.err는 정적 최종 필드이며, 레거시 이유로 System.setIn, System.setOut 및 System.setErr 메소드로 변경할 수 있어야합니다. 이러한 필드는 일반 최종 필드와 구분하기 위해 쓰기 방지 된 것으로 간주합니다.
출처 : http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.4
답변
또한 joor 라이브러리 와 통합했습니다.
그냥 사용
Reflect.on(yourObject).set("finalFieldName", finalFieldValue);
또한 override
이전 솔루션이 누락 된 문제를 수정했습니다 . 그러나 다른 좋은 해결책이 없을 때만 이것을 매우 신중하게 사용하십시오.
답변
최고 순위의 답변과 함께 약간 간단한 접근 방식을 사용할 수 있습니다. Apache Commons FieldUtils
클래스에는 이미 작업을 수행 할 수있는 특정 메소드가 있습니다. FieldUtils.removeFinalModifier
방법을 살펴보십시오 . 대상 필드 인스턴스와 내게 필요한 옵션 강제 플래그를 지정해야합니다 (비공개 필드로 재생하는 경우). 자세한 내용은 여기를 참조하십시오 .
답변
보안 관리자가있는 경우 사용할 수 있습니다 AccessController.doPrivileged
위의 허용 된 답변에서 동일한 예를 보았습니다.
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
// wrapping setAccessible
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
modifiersField.setAccessible(true);
return null;
}
});
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
람다 식에서을 다음과 AccessController.doPrivileged
같이 단순화 할 수 있습니다.
AccessController.doPrivileged((PrivilegedAction) () -> {
modifiersField.setAccessible(true);
return null;
});
답변
허용 된 답변은 JDK 1.8u91에 배포 될 때까지 효과적이었습니다. 그런 다음 field.set(null, newValue);
호출하기 전에 리플렉션을 통해 값을 읽었을 때 라인 에서 실패했음을 깨달았습니다.setFinalStatic
메서드 .
아마도 읽기로 인해 Java 리플렉션 내부 설정이 다르게 설정 sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl
되었습니다 (즉 , 실패하는 경우)sun.reflect.UnsafeStaticObjectFieldAccessorImpl
지만 , 성공 사례가 사례) 더 자세히 설명하지는 않았습니다.
이전 값을 기반으로 새 값을 임시로 설정하고 나중에 이전 값을 다시 설정해야했기 때문에 서명 기능을 약간 변경하여 외부에서 계산 기능을 제공하고 이전 값을 반환했습니다.
public static <T> T assignFinalField(Object object, Class<?> clazz, String fieldName, UnaryOperator<T> newValueFunction) {
Field f = null, ff = null;
try {
f = clazz.getDeclaredField(fieldName);
final int oldM = f.getModifiers();
final int newM = oldM & ~Modifier.FINAL;
ff = Field.class.getDeclaredField("modifiers");
ff.setAccessible(true);
ff.setInt(f,newM);
f.setAccessible(true);
T result = (T)f.get(object);
T newValue = newValueFunction.apply(result);
f.set(object,newValue);
ff.setInt(f,oldM);
return result;
} ...
그러나 일반적인 경우에는 이것으로 충분하지 않습니다.