익명 클래스에서 최종 변수에만 액세스 할 수있는 이유는 무엇입니까?
a여기서 만 최종적 일 수 있습니다. 왜? 어떻게 재 할당 할 수 있습니다a에onClick()개인 회원으로 그것을 유지하지 않고 방법은?private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); }5 * a클릭했을 때 어떻게 반환 합니까? 내말은,private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }
주석에서 언급했듯이, 일부는 Java 8에서 관련이 final없으며 암시적일 수 있습니다. 익명의 내부 클래스 또는 람다 식 에는 효과적으로 최종 변수 만 사용할 수 있습니다.
기본적으로 Java가 클로저를 관리하는 방식 때문 입니다.
익명 내부 클래스의 인스턴스를 만들 때 해당 클래스 내에서 사용되는 모든 변수 는 자동 생성 생성자를 통해 값이 복사됩니다. 이렇게하면 컴파일러가 "로컬 변수"의 논리적 상태를 유지하기 위해 다양한 추가 유형을 자동 생성하지 않아도됩니다. 예를 들어 C # 컴파일러는 다음과 같습니다 ... (C #은 익명 함수에서 변수를 캡처 할 때 실제로 변수를 캡처합니다. 클로저는 메소드의 본문에서 볼 수있는 방식으로 변수를 업데이트 할 수 있으며 그 반대도 마찬가지입니다.)
값이 익명 내부 클래스의 인스턴스에 복사되었으므로 변수가 나머지 메소드로 수정 될 수 있으면 이상하게 보일 것입니다. 그 효과적으로 무엇 때문에 할 일이있을 ... 당신이 다른 시간에 촬영 한 사본)와 함께 일하게 될 것입니다. 마찬가지로 익명의 내부 클래스 내에서 변경을 수행 할 수있는 경우 개발자는 이러한 변경 사항이 엔 클로징 메서드 본문 내에 표시 될 것으로 기대할 수 있습니다.
변수를 final로 설정하면 이러한 모든 가능성이 제거됩니다. 값을 전혀 변경할 수 없으므로 그러한 변경이 표시 될지 걱정할 필요가 없습니다. 메소드와 익명의 내부 클래스가 서로의 변경 사항을 볼 수있게하는 유일한 방법은 변경 가능한 유형의 설명을 사용하는 것입니다. 이것은 둘러싸는 클래스 자체, 배열, 변경 가능한 래퍼 유형 등이 될 수 있습니다. 기본적으로 그것은 한 메소드와 다른 메소드 사이의 통신과 비슷합니다. 한 메소드의 매개 변수 에 대한 변경 사항은 호출자 가 볼 수 없지만 매개 변수가 참조 하는 객체의 변경 사항 은 볼 수 있습니다.
Java와 C # 클로저의보다 자세한 비교에 관심이 있다면 더 자세히 다루는 기사 가 있습니다. 이 답변에서 Java 측에 집중하고 싶었습니다. :)
익명 클래스가 외부 범위의 데이터를 업데이트 할 수있는 트릭이 있습니다.
private void f(Button b, final int a) {
final int[] res = new int[1];
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
res[0] = a * 5;
}
});
// But at this point handler is most likely not executed yet!
// How should we now res[0] is ready?
}
그러나이 트릭은 동기화 문제로 인해 좋지 않습니다. 핸들러가 나중에 호출되면 1) 핸들러가 다른 스레드에서 호출 된 경우 re에 대한 액세스를 동기화해야합니다. 2) res가 업데이트되었음을 나타내는 일종의 플래그 또는 표시가 있어야합니다.
그러나 익명 클래스가 동일한 스레드에서 즉시 호출되면이 트릭은 정상적으로 작동합니다. 처럼:
// ...
final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);
// ...
익명 클래스는 내부 클래스 이며 엄격한 규칙은 내부 클래스 (JLS 8.1.3)에 적용됩니다 .
내부 클래스에서 선언되었지만 선언되지 않은 로컬 변수, 공식 메서드 매개 변수 또는 예외 처리기 매개 변수 는 final로 선언해야합니다 . 내부 클래스에서 사용되었지만 선언되지 않은 로컬 변수는 내부 클래스 의 본문 앞에 반드시 지정해야합니다 .
jls 또는 jvm에 대한 이유 또는 설명을 아직 찾지 못했지만 컴파일러는 각 내부 클래스에 대해 별도의 클래스 파일을 작성하고이 클래스 파일에 선언 된 메소드가 있는지 확인해야합니다 ( 바이트 코드 수준에서)는 적어도 지역 변수 값에 액세스 할 수 있습니다.
( Jon은 완전한 대답을 가지고 있습니다-JLS 규칙에 관심이있을 수 있으므로 삭제하지 마십시오)
클래스 레벨 변수를 작성하여 리턴 값을 얻을 수 있습니다. 내말은
class A {
int k = 0;
private void f(Button b, int a){
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
k = a * 5;
}
});
}
이제 K의 가치를 얻고 원하는 곳에서 사용할 수 있습니다.
이유는 다음과 같습니다.
로컬 내부 클래스 인스턴스는 Main 클래스에 연결되며 포함하는 메서드의 최종 로컬 변수에 액세스 할 수 있습니다. 인스턴스가 포함하는 메소드의 최종 로컬을 사용하는 경우 변수가 범위를 벗어난 경우에도 변수는 인스턴스 작성시 보유한 값을 유지합니다 (이는 사실상 Java의 조잡하고 제한된 클로저 버전입니다).
로컬 내부 클래스는 클래스 나 패키지의 멤버가 아니므로 액세스 수준으로 선언되지 않습니다. (단, 자신의 멤버는 일반 클래스와 같은 액세스 레벨을 갖습니다.)
Java에서 변수는 매개 변수뿐만 아니라 클래스 수준 필드와 같이 최종 변수가 될 수 있습니다.
public class Test
{
public final int a = 3;
또는 같은 지역 변수로
public static void main(String[] args)
{
final int a = 3;
익명 클래스에서 변수에 액세스하고 변수를 수정하려면 해당 변수를 둘러싸는 클래스의 클래스 수준 변수 로 만들 수 있습니다 .
public class Test
{
public int a;
public void doSomething()
{
Runnable runnable =
new Runnable()
{
public void run()
{
System.out.println(a);
a = a+1;
}
};
}
}
당신은 마지막으로 변수를 가질 수 없습니다 그리고 그것에게 새로운 가치를 제공합니다. final그 의미는 다음과 같습니다. 값이 변경 불가능하고 최종적입니다.
마지막으로 Java는 로컬 익명 클래스에 안전하게 복사 할 수 있습니다. int에 대한 참조 를 얻지 못합니다 (특히 Java의 int와 같은 프리미티브에 대한 참조를 가질 수 없기 때문에 Objects에 대한 참조 만 가능하기 때문에 ).
a의 값을 익명 클래스에서 a라는 암시 적 int로 복사합니다.
액세스가 로컬 최종 변수로만 제한되는 이유는 모든 로컬 변수에 액세스 할 수있는 경우 먼저 내부 클래스가 액세스 할 수있는 별도의 섹션에 복사해야하고 여러 사본을 유지 보수해야하기 때문입니다. 가변 지역 변수는 데이터가 일치하지 않을 수 있습니다. 최종 변수는 불변이므로 변수에 대한 사본의 수는 데이터 일관성에 영향을 미치지 않습니다.
이 제한의 이론적 근거를 이해하려면 다음 프로그램을 고려하십시오.
public class Program {
interface Interface {
public void printInteger();
}
static Interface interfaceInstance = null;
static void initialize(int val) {
class Impl implements Interface {
@Override
public void printInteger() {
System.out.println(val);
}
}
interfaceInstance = new Impl();
}
public static void main(String[] args) {
initialize(12345);
interfaceInstance.printInteger();
}
}
interfaceInstance는 애프터 메모리에 남아 초기화 메소드가 리턴하지만, 매개 변수의 발은 하지 않습니다. JVM은 범위 외부의 로컬 변수에 액세스 할 수 없으므로 Java는 val 값을 interfaceInstance 내에서 동일한 이름의 내재 된 필드 에 복사하여 printInteger에 대한 후속 호출을 수행합니다 . interfaceInstance는 것으로 알려져 캡처 로컬 파라미터의 값. 매개 변수가 최종 (또는 효과적으로 최종)이 아닌 경우 값이 변경되어 캡처 된 값과 동기화되지 않아 직관적이지 않은 동작이 발생할 수 있습니다.
익명의 내부 클래스 내의 메소드는 생성 된 스레드가 종료 된 후에 호출 될 수 있습니다. 귀하의 예제에서 내부 클래스는 이벤트 발송 스레드에서 호출되며 생성 된 스레드와 동일한 스레드가 아닙니다. 따라서 변수의 범위가 달라집니다. 따라서 이러한 변수 할당 범위 문제를 보호하려면이를 최종적으로 선언해야합니다.
익명의 내부 클래스가 메소드 본문 내에 정의되면 해당 메소드의 범위에서 final로 선언 된 모든 변수는 내부 클래스에서 액세스 할 수 있습니다. 스칼라 값의 경우, 일단 지정되면 최종 변수의 값을 변경할 수 없습니다. 객체 값의 경우 참조를 변경할 수 없습니다. 이를 통해 Java 컴파일러는 런타임시 변수 값을 "캡처"하고 사본을 내부 클래스의 필드로 저장할 수 있습니다. 외부 메소드가 종료되고 스택 프레임이 제거되면 원래 변수는 사라지지만 내부 클래스의 개인 사본은 클래스의 자체 메모리에 유지됩니다.
( http://en.wikipedia.org/wiki/Final_%28Java%29 )
private void f(Button b, final int a[]) {
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
a[0] = a[0] * 5;
}
});
}
마찬가지로 존은 이 구현 세부 사항은 다른 가능한 대답은 JVM이 자신의 활성화를 종료 한 레코드 쓰기를 처리하지 않는 것이 답합니다.
적용되는 대신 람다가 어느 곳에 저장되어 나중에 실행되는 유스 케이스를 고려하십시오.
스몰 토크에서는 그러한 수정을하면 불법 상점이 생겼다는 것을 기억합니다.
이 코드를 사용해보십시오
배열 목록을 만들고 그 안에 값을 넣고 그것을 반환하십시오 :
private ArrayList f(Button b, final int a)
{
final ArrayList al = new ArrayList();
b.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
int b = a*5;
al.add(b);
}
});
return al;
}
어쩌면이 트릭은 당신에게 아이디어를 줄 것입니다.
Boolean var= new anonymousClass(){
private String myVar; //String for example
@Overriden public Boolean method(int i){
//use myVar and i
}
public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);
'development' 카테고리의 다른 글
| 쿼리 문자열 매개 변수는 HTTPS (HTTP + SSL)에서 안전합니까? (0) | 2020.03.03 |
|---|---|
| ArrayList와 Vector의 차이점은 무엇입니까? (0) | 2020.03.03 |
| jQuery Validate Plugin-간단한 커스텀 규칙을 만드는 방법? (0) | 2020.03.03 |
| CSS에서 px 또는 rem 값 단위를 사용해야합니까? (0) | 2020.03.03 |
| 지속적인 통합 및 지속적인 배포 및 지속적인 배포 (0) | 2020.03.02 |