익명 클래스에 매개 변수를 전달하는 방법은 무엇입니까?


146

익명 클래스에 매개 변수를 전달하거나 외부 매개 변수에 액세스 할 수 있습니까? 예를 들면 다음과 같습니다.

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
    }
});

리스너가 실제 이름 지정된 클래스로 리스너를 작성하지 않고 myVariable에 액세스하거나 myVariable에 전달할 수있는 방법이 있습니까?


7
final클로징 방법에서 로컬 변수를 참조 할 수 있습니다 .
Tom Hawtin-tackline

개인 myVariable 인스턴스를 초기화하고 반환으로 인해 닫는 중괄호에서 호출 할 수있는 개인 메서드를 정의하는 Adam Mmlodzinski의 제안을 좋아합니다 this.
dlamblin

이 질문의 일부 공유 목적이 있습니다 stackoverflow.com/questions/362424/...을
알라 맥코맥에게

익명 클래스 내부에서 전역 클래스 변수를 사용할 수도 있습니다. 아마도 그리 깨끗하지는 않지만 일을 할 수 있습니다.
Jori

답변:


78

익명 클래스는 생성자를 가질 수 없기 때문에 기술적으로는 아닙니다.

그러나 클래스는 범위를 포함하여 변수를 참조 할 수 있습니다. 익명 클래스의 경우 포함 클래스의 인스턴스 변수 또는 final로 표시된 로컬 변수 일 수 있습니다.

편집 : Peter가 지적했듯이 매개 변수를 익명 클래스의 수퍼 클래스 생성자에게 전달할 수도 있습니다.


21
익명 클래스 사용은 부모의 생성자를 사용합니다. 예new ArrayList(10) { }
Peter Lawrey

좋은 지적. 따라서 매개 변수를 제어 할 수는 없지만 익명 클래스에 매개 변수를 전달하는 또 다른 방법입니다.
Matthew Willis

익명 클래스는 생성자가 필요하지 않음
newacct

4
익명 클래스에는 인스턴스 이니셜 라이저가있을 수 있으며 익명 클래스에서 매개 변수없는 생성자로 작동 할 수 있습니다. 이들은 필드 할당과 같은 순서로, 즉 super()실제 생성자의 나머지 이후에 실행됩니다 . new someclass(){ fields; {initializer} fields; methods(){} }. 정적 초기화 도구와 비슷하지만 정적 키워드가 없습니다. docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6
Mark Jeronimus

stackoverflow.com/a/3045185/1737819에서 생성자없이 구현하는 방법을 설명합니다.
개발자 Marius Žilėnas

336

예, 'this'를 반환하는 초기화 메소드를 추가하고 즉시 해당 메소드를 호출하면됩니다.

int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    private int anonVar;
    public void actionPerformed(ActionEvent e) {
        // How would one access myVariable here?
        // It's now here:
        System.out.println("Initialized with value: " + anonVar);
    }
    private ActionListener init(int var){
        anonVar = var;
        return this;
    }
}.init(myVariable)  );

'최종'선언이 필요하지 않습니다.


4
와우 ... 훌륭해! final익명 클래스에 정보를 얻을 수 있도록 참조 객체 를 만드는 데 지쳤습니다 . 공유해 주셔서 감사합니다!
매트 클라인

7
init()함수가 왜 반환 this해야합니까? 나는 구문을 실제로 얻지 못한다.
Jori

11
myButton.addActionListener (...)는 ActionListener 객체를 메소드 호출시 반환되는 객체로 예상하기 때문입니다.

1
나는 추측한다. 그것은 비록 그것이 작동하지만 오히려 추악한 자신을 발견. 필요한 변수와 함수 매개 변수를 최종적으로 만들고 내부 클래스에서 직접 참조 할 수있는 대부분의 시간을 알았습니다. 보통 읽기 만하 기 때문입니다.
Thomas

2
더 간단 : private int anonVar = myVariable;
Anm

29

예. 내부 클래스에서 볼 수있는 변수를 캡처 할 수 있습니다. 유일한 한계는 그것이 최종적 이어야한다는 것입니다


익명 클래스에서 참조 된 인스턴스 변수는 최종 변수 일 필요는 없습니다.
Matthew Willis

8
인스턴스 변수 this는 최종 변수를 통해 참조됩니다 .
Peter Lawrey

변수를로 변경하지 않으려면 어떻게해야 final합니까? 대안을 찾을 수 없습니다. 이것은 설계된 오리진 파라미터에 영향을 줄 수 있습니다 final.
Alston

20

이처럼 :

final int myVariable = 1;

myButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // Now you can access it alright.
    }
});

14

이것은 마술을 할 것입니다

int myVariable = 1;

myButton.addActionListener(new ActionListener() {

    int myVariable;

    public void actionPerformed(ActionEvent e) {
        // myVariable ...
    }

    public ActionListener setParams(int myVariable) {

        this.myVariable = myVariable;

        return this;
    }
}.setParams(myVariable));

8

http://www.coderanch.com/t/567294/java/java/declare-constructor-anonymous-class에 표시된 것처럼 인스턴스 이니셜 라이저를 추가 할 수 있습니다. 이름이없고 생성자와 마찬가지로 먼저 실행되는 블록입니다.

왜 Java 인스턴스 초기화 프로그램인가? 에서 논의 된 것처럼 보입니다 . 인스턴스 이니셜 라이저는 생성자어떻게 다릅니 까? 생성자와의 차이점에 대해 설명합니다.


이 질문은 해결되지 않습니다. 여전히 지역 변수에 액세스하는 데 문제가 있으므로 Adam Mlodzinski 또는 adarshr의 솔루션을 사용해야합니다.
Matt Klein

1
@MattKlein 나에게 해결하는 것처럼 보입니다. 실제로는 동일하지만 덜 장황합니다.
haelix

1
질문은 매개 변수가 필요한 생성자를 사용하는 것처럼 매개 변수를 클래스에 전달하는 방법을 알고 싶었습니다. 링크 (여기에서 요약해야 함)에는 매개 변수가없는 인스턴스 이니셜 라이저가있는 방법 만 표시되어 질문에 대답하지 않습니다. 이 기술은 finalaav에 설명 된대로 변수 와 함께 사용할 수 있지만 해당 정보는이 답변에 제공되지 않았습니다. 지금까지 가장 좋은 대답은 Adam Mlodzinksi가 제공 한 답변입니다 (이제이 패턴을 더 이상 결승에 독점적으로 사용하지 않습니다). 나는 이것이 질문에 대답하지 않는다는 나의 의견을지지한다.
매트 클라인

7

내 솔루션은 구현 된 익명 클래스를 반환하는 메서드를 사용하는 것입니다. 정규 인수는 메소드에 전달 될 수 있으며 익명 클래스 내에서 사용 가능합니다.

예를 들면 다음과 같습니다 (일부 GWT 코드에서 텍스트 상자 변경 처리).

/* Regular method. Returns the required interface/abstract/class
   Arguments are defined as final */
private ChangeHandler newNameChangeHandler(final String axisId, final Logger logger) {

    // Return a new anonymous class
    return new ChangeHandler() {
        public void onChange(ChangeEvent event) {
            // Access method scope variables           
            logger.fine(axisId)
        }
     };
}

이 예제에서 새로운 익명 클래스 메소드는 다음과 같이 참조됩니다.

textBox.addChangeHandler(newNameChangeHandler(myAxisName, myLogger))

또는 OP의 요구 사항을 사용하여 :

private ActionListener newActionListener(final int aVariable) {
    return new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            System.out.println("Your variable is: " + aVariable);
        }
    };
}
...
int myVariable = 1;
newActionListener(myVariable);

이것은 익명 클래스를 식별하기 쉬운 몇 가지 변수로 제한하고 일부 변수를 최종적으로 만들어야 할 필요성을 제거합니다.
불가능한 변수

3

다른 사람들은 이미 익명 클래스가 최종 변수에만 액세스 할 수 있다고 대답했습니다. 그러나 그들은 원래 변수를 최종적으로 유지하는 방법에 대한 의문을 열어 둡니다. Adam Mlodzinski 는 해결책을 주었지만 꽤 부풀어 있습니다. 이 문제에 대한 훨씬 간단한 해결책이 있습니다.

myVariable최종적 이지 않으려면 그것이 중요하지 않은 새로운 범위로 포장해야합니다.

int myVariable = 1;

{
    final int anonVar = myVariable;

    myButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            // How would one access myVariable here?
            // Use anonVar instead of myVariable
        }
    });
}

Adam Mlodzinski는 자신의 답변에서 더 많은 코드를 사용하여 다른 작업을 수행하지 않습니다.


이것은 여전히 ​​추가 범위없이 작동합니다. final을 사용하는 다른 답변과 사실상 동일합니다.
Adam Mlodzinski

@AdamMlodzinski 아니요 개인 범위에서 원래 변수의 값을 가진 새 변수를 도입하기 때문에 실제로 답변과 동일합니다.
21시 33 분

실제로 동일하지 않습니다. 귀하의 경우 내부 클래스가 anonVar를 변경할 수 없으므로 효과가 다릅니다. 내부 클래스가 상태를 유지 해야하는 경우 코드는 프리미티브가 아닌 setter와 함께 일종의 Object를 사용해야합니다.
Adam Mlodzinski

@AdamMlodzinski 그것은 질문이 아닙니다. 문제는 외부 변수에 최종적으로 액세스하지 않고 액세스하는 방법이었습니다. 그리고 해결책은 최종 사본을 만드는 것입니다. 물론 리스너에서 변수의 추가 가변 복사본을 만들 수 있음이 분명합니다. 그러나 먼저 요청하지 않았으며 두 번째 init방법 은 필요하지 않습니다 . 이 추가 변수를 갖기 위해 예제에 한 줄의 코드를 추가 할 수 있습니다. 빌더 패턴의 팬이라면 자유롭게 사용할 수 있지만이 경우에는 필요하지 않습니다.
18시

final변수 솔루션을 사용하는 것과 이것이 어떻게 다른지 알 수 없습니다 .
Kevin Rave

3

일반 람다 를 사용할 수 있습니다 ( "람다 표현식은 변수를 캡처 할 수 있습니다")

int myVariable = 1;
ActionListener al = ae->System.out.println(myVariable);
myButton.addActionListener( al );

또는 심지어 기능

Function<Integer,ActionListener> printInt = 
    intvar -> ae -> System.out.println(intvar);

int myVariable = 1;
myButton.addActionListener( printInt.apply(myVariable) );

함수 사용은 데코레이터와 어댑터를 리팩터링하는 좋은 방법입니다. 여기를 참조하십시오.

방금 람다에 대해 배우기 시작했기 때문에 실수를 발견하면 자유롭게 의견을 쓰십시오.


1

외부 변수 (익명 클래스에 속하지 않음)에 값을 넣는 간단한 방법은 얼마나 간단합니까!

같은 방법으로 외부 변수의 값을 얻으려면 원하는 것을 반환하는 메서드를 만들 수 있습니다!

public class Example{

    private TypeParameter parameter;

    private void setMethod(TypeParameter parameter){

        this.parameter = parameter;

    }

    //...
    //into the anonymus class
    new AnonymusClass(){

        final TypeParameter parameterFinal = something;
        //you can call setMethod(TypeParameter parameter) here and pass the
        //parameterFinal
        setMethod(parameterFinal); 

        //now the variable out the class anonymus has the value of
        //of parameterFinal

    });

 }

-2

익명 클래스는 기본적으로 람다와 같지만 구문이 좋지 않습니다 ... 이것은 사실이지만 구문이 더 나쁘고 로컬 변수가 포함 클래스로 유출되도록합니다.

부모 클래스의 필드로 만들어 최종 변수에 액세스 할 수 없습니다.

예 :

상호 작용:

public interface TextProcessor
{
    public String Process(String text);
}

수업:

private String _key;

public String toJson()
{
    TextProcessor textProcessor = new TextProcessor() {
        @Override
        public String Process(String text)
        {
            return _key + ":" + text;
        }
    };

    JSONTypeProcessor typeProcessor = new JSONTypeProcessor(textProcessor);

    foreach(String key : keys)
    {
        _key = key;

        typeProcessor.doStuffThatUsesLambda();
    }

그들이 자바 8에서 이것을 분류했는지 알지 못합니다 (EE 세계에 갇혀 있고 아직 8을 얻지 못했습니다). 그러나 C #에서는 다음과 같이 보일 것입니다 :

    public string ToJson()
    {
        string key = null;
        var typeProcessor = new JSONTypeProcessor(text => key + ":" + text);

        foreach (var theKey in keys)
        {
            key = theKey;

            typeProcessor.doStuffThatUsesLambda();
        }
    }

C #에서 별도의 인터페이스가 필요하지 않습니다. Java에서 더 나쁜 디자인을 만들고 더 많은 것을 반복한다는 것을 알았습니다. 자바를 재사용하기 위해 Java에 추가 해야하는 코드 + 복잡성이 많은 시간을 복사하여 붙여 넣는 것보다 나쁩니다.


여기에 언급 된 다른 해킹 같은 외모는 당신이 사용하는 하나 개의 요소 배열을하는 것입니다 수 있습니다 stackoverflow.com/a/4732586/962696
JonnyRaa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.