Java에서 익명 내부 클래스는 어떻게 사용됩니까?


309

Java에서 익명 클래스의 사용은 무엇입니까? 익명 클래스의 사용이 Java의 장점 중 하나라고 말할 수 있습니까?


67
Java의 클로저 부족을 해결할 수있는 방법이므로 Java의 이점은별로 없습니다.
Eric Wilson

4
Java 8은 Lambda 표현식도 도입했습니다. 답변 확인 : stackoverflow.com/questions/355167/…
akhil_mittal

답변:


369

"익명 클래스"란 익명의 내부 클래스 를 의미 합니다.

익명의 내부 클래스는 클래스를 실제로 서브 클래스하지 않고도 메소드 재정의와 같은 특정 "엑스트라"를 가진 객체의 인스턴스를 만들 때 유용 할 수 있습니다.

이벤트 리스너를 연결하는 바로 가기로 사용하는 경향이 있습니다.

button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        // do something
    }
});

이 메소드를 사용하면 구현하는 추가 클래스를 만들 필요가 없으므로 코딩이 조금 빨라 ActionListener집니다. 실제로 별도의 클래스를 만들지 않고도 익명의 내부 클래스를 인스턴스화 할 수 있습니다.

나는이 기술을 전체 수업을 불필요하게하는 "신속하고 더러운"작업에만 사용합니다. 정확히 같은 일을 수행하는 여러 익명의 내부 클래스가 있으면 실제 클래스, 즉 내부 클래스 또는 별도의 클래스로 리팩토링되어야합니다.


5
또는 익명의 내부 클래스를 사용하여 익명의 내부 클래스를 하나의 메소드로 리팩토링 할 수 있습니다 (아마도 중복 된 다른 코드).
Tom Hawtin-tackline

3
좋은 답변이지만 빠른 질문입니다. Java가 익명의 내부 클래스없이 살 수 있고 선택할 수있는 추가 옵션과 같은 것을 의미합니까?
realPK

5
매우 잘 설명되어 있지만, 심지어 나는 이것을 읽는 사람이라면 누구나 Java 8 및 람다식이 코딩을 더 빠르고 읽기 쉽게 만들기 위해 무엇을 할 수 있는지 볼 것을 제안 할 것입니다.
Pievis

2
@ user2190639 정확하게, Java8 에서 Lambda
bonCodigo

3
왜 뭐라고 overloading methods하지 overriding methods?
Tarun

73

익명의 내부 클래스는 효과적으로 닫히므로 람다 식 또는 "위임"을 에뮬레이트하는 데 사용할 수 있습니다. 예를 들어 다음 인터페이스를 사용하십시오.

public interface F<A, B> {
   B f(A a);
}

이것을 익명으로 사용하여 Java에서 일급 함수 를 작성할 수 있습니다 . 주어진 목록에서 i보다 큰 첫 번째 숫자를 반환하거나 숫자가 크지 않은 경우 i를 반환하는 다음과 같은 메서드가 있다고 가정 해 봅시다.

public static int larger(final List<Integer> ns, final int i) {
  for (Integer n : ns)
     if (n > i)
        return n;
  return i;
}

그런 다음 주어진 목록에서 i보다 작은 첫 번째 숫자를 반환하거나 더 작은 숫자가없는 경우 i를 반환하는 다른 방법이 있습니다.

public static int smaller(final List<Integer> ns, final int i) {
   for (Integer n : ns)
      if (n < i)
         return n;
   return i;
}

이 방법들은 거의 동일합니다. 일급 함수 유형 F를 사용하여 다음과 같이 하나의 메소드로 다시 작성할 수 있습니다.

public static <T> T firstMatch(final List<T> ts, final F<T, Boolean> f, T z) {
   for (T t : ts)
      if (f.f(t))
         return t;
   return z;
}

익명 클래스를 사용하여 firstMatch 메소드를 사용할 수 있습니다.

F<Integer, Boolean> greaterThanTen = new F<Integer, Boolean> {
   Boolean f(final Integer n) {
      return n > 10;
   }
};
int moreThanMyFingersCanCount = firstMatch(xs, greaterThanTen, x);

이것은 실제로 고안된 예이지만, 값처럼 함수를 전달할 수 있다는 것은 매우 유용한 기능이라는 것을 쉽게 알 수 있습니다. Joel 자신의 "프로그래밍 언어가이를 수행 할 수 있습니까?"를 참조하십시오 .

이 스타일의 Java 프로그래밍을위한 멋진 라이브러리 : Functional Java.


20
불행히도 Java, IMHO에서 함수형 프로그래밍을 수행하는 것의 장황은 그 이점을 능가합니다. 함수형 프로그래밍의 가장 큰 장점 중 하나는 코드 크기를 줄이고 경향을 파악하고 더 쉽게 읽고 수정할 수 있다는 것입니다. 그러나 함수형 자바는 전혀 그렇게하지 않는 것 같습니다.
Chii

27
Java의 간결함과 함께 함수형 프로그래밍의 모든 이해도!
Adam Jaskiewicz

3
내 경험상 Java의 기능적 스타일은 선결제로 지불되지만 장기적으로는 간결하게 나타납니다. 예를 들어, myList.map (f)는 해당 for-loop보다 덜 장황합니다.
Apocalisp

2
기능적 프로그래밍 스타일 언어 인 Scala 는 JVM 내부에서 잘 작동하며 기능적 프로그래밍 시나리오를위한 옵션 일 수 있습니다.
Darrell Teague

51

익명의 내부 클래스는 다음 시나리오에서 사용됩니다.

1.) 재정의 (하위 분류)의 경우 현재 케이스를 제외하고 클래스 정의를 사용할 수없는 경우 :

class A{
   public void methodA() {
      System.out.println("methodA");
    }
}
class B{
    A a = new A() {
     public void methodA() {
        System.out.println("anonymous methodA");
     }
   };
}

2.) 인터페이스 구현이 현재의 경우에만 인터페이스 구현이 필요한 경우 :

interface interfaceA{
   public void methodA();
}
class B{
   interfaceA a = new interfaceA() {
     public void methodA() {
        System.out.println("anonymous methodA implementer");
     }
   };
}

3.) 인수 정의 익명 내부 클래스 :

 interface Foo {
   void methodFoo();
 }
 class B{
  void do(Foo f) { }
}

class A{
   void methodA() {
     B b = new B();
     b.do(new Foo() {
       public void methodFoo() {
         System.out.println("methodFoo");
       } 
     });
   } 
 } 

8
멋진 대답, 3. 같은 외모는) 이벤트 리스너에 사용되는 패턴
XDL

47

때때로 맵 인스턴스화를위한 구문 핵으로 사용합니다.

Map map = new HashMap() {{
   put("key", "value");
}};

vs

Map map = new HashMap();
map.put("key", "value");

많은 put 문을 수행 할 때 중복성을 줄입니다. 그러나 외부 클래스를 원격으로 직렬화해야 할 때이 문제가 발생했습니다.


56
분명히 첫 번째 중괄호 세트는 익명 내부 클래스 (하위 클래스 하위 클래스)입니다. 두 번째 중괄호 세트는 정적 초기화가 아닌 인스턴스 이니셜 라이저로, HashMap 서브 클래스에 값을 설정합니다. 멍청한 자들에게 철자를 쓰지 않으면 +1입니다. ;-D
스펜서 Kormos

4
이중 괄호 구문에 대한 자세한 내용은 여기를 참조하십시오 .
Martin Andersson


18

일반적으로 장황한 콜백 형태로 사용됩니다.

나는 그들이 그것을 가지고 있지 않고 매번 명명 된 클래스를 만들어야하는 것에 비해 이점이라고 말할 수 있다고 가정하지만 비슷한 개념은 다른 언어 (클로저 또는 블록)에서 훨씬 잘 구현됩니다.

다음은 스윙 예제입니다

myButton.addActionListener(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
        // do stuff here...
    }
});

여전히 장황하지만 장황하지만 모든 상황에서 리스너에 대해 명명 된 클래스를 정의하는 것보다 훨씬 좋습니다 (상황과 재사용에 따라 다르지만 여전히 더 나은 방법 일 수 있습니다)


1
간결하다고 말했습니까? 장황한 경우 콜백은 별도로 유지되어 조금 더 커져서 장황하게 만듭니다. 이것이 여전히 장황하다고 말하면 간결한 형태는 무엇입니까?
user3081519

1
@ user3081519, 비슷 myButton.addActionListener(e -> { /* do stuff here */})하거나 더 겁이납니다 myButton.addActionListener(stuff).
사무엘 에드윈 워드

8

리스너, 런너 블 (스레드 생성) 등 다른 함수 내부의 특정 목적을 위해 클래스를 작성해야하는 상황에서 사용합니다.

아이디어는 함수 코드 내부에서 호출하므로 다른 곳에서는 참조하지 않으므로 이름을 지정할 필요가 없습니다. 컴파일러는 그것들을 열거합니다.

그것들은 본질적으로 구문 설탕이며 일반적으로 더 커지면 다른 곳으로 옮겨야합니다.

Java의 장점 중 하나인지 확실하지 않지만, Java를 사용하면 (그리고 불행히도 우리는 모두 자주 사용하지만) 하나라고 주장 할 수 있습니다.


6

익명 클래스를위한 가이드 라인.

  1. 익명 클래스가 동시에 선언되고 초기화됩니다.

  2. 익명 클래스는 하나의 클래스 또는 인터페이스 resp로만 확장하거나 구현해야합니다.

  3. 익명 클래스에는 이름이 없으므로 한 번만 사용할 수 있습니다.

예 :

button.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub

    }
});

# 3에 관하여 : 완전히 사실은 아닙니다. 리플렉션이있는 익명 클래스의 여러 인스턴스를 얻을 수 있습니다 (예 :) ref.getClass().newInstance().
icza

규칙은 질문에 대답하지 않습니다.
Lorne의 후작

5

예, 익명의 내부 클래스는 분명히 Java의 장점 중 하나입니다.

익명의 내부 클래스를 사용하면 주변 클래스의 최종 및 멤버 변수에 액세스 할 수 있으며 리스너 등에 유용합니다.

그러나 가장 큰 장점은 주변 클래스 / 방법 / 블록에 밀접하게 연결되어있는 내부 클래스 코드가 특정 컨텍스트 (주변 클래스, 방법 및 블록)를 가지고 있다는 것입니다.


1
주변 클래스에 액세스하는 것이 매우 중요합니다! 익명 클래스가 사용되는 많은 경우에 이것이 이유라고 생각합니다. 왜냐하면 (별도의 클래스를 사용하는 경우) 외부 클래스 / 메소드의 비공개 속성, 메소드 및 로컬 변수가 필요 / 사용해야하기 때문입니다. 통과 또는 출판.
icza

5
new Thread() {
        public void run() {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                System.out.println("Exception message: " + e.getMessage());
                System.out.println("Exception cause: " + e.getCause());
            }
        }
    }.start();

이것은 스레드를 사용하는 익명의 내부 유형에 대한 예 중 하나입니다.


3

새 스레드를 호출하기 위해 익명 객체를 사용합니다.

new Thread(new Runnable() {
    public void run() {
        // you code
    }
}).start();

3

내부 클래스는 외부 클래스의 인스턴스와 관련된 두 개의 특별한 종류가 있습니다 : 로컬 클래스와 익명 클래스 . 익명 클래스를 사용하면 클래스를 동시에 선언하고 인스턴스화 할 수 있으므로 코드가 간결 해집니다. 이름이 없기 때문에 로컬 클래스가 한 번만 필요할 때 사용합니다.

클래스 가있는 doc 의 예를 고려하십시오 Person.

public class Person {

    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    public int getAge() {
        // ...
    }

    public void printPerson() {
        // ...
    }
}

검색 기준과 일치하는 멤버를 다음과 같이 인쇄하는 방법이 있습니다.

public static void printPersons(
    List<Person> roster, CheckPerson tester) {
    for (Person p : roster) {
        if (tester.test(p)) {
            p.printPerson();
        }
    }
}

다음 CheckPerson과 같은 인터페이스가 있습니다.

interface CheckPerson {
    boolean test(Person p);
}

이제이 인터페이스를 구현하는 익명 클래스를 사용하여 검색 기준을 다음과 같이 지정할 수 있습니다.

printPersons(
    roster,
    new CheckPerson() {
        public boolean test(Person p) {
            return p.getGender() == Person.Sex.MALE
                && p.getAge() >= 18
                && p.getAge() <= 25;
        }
    }
);

여기서 인터페이스는 매우 간단하며 익명 클래스의 구문은 다루기 쉽고 불분명합니다.

Java 8 에는 단 하나의 추상 메소드가있는 인터페이스 인 Functional Interface 라는 용어가 도입 되었으므로 CheckPerson기능적 인터페이스 라고 할 수 있습니다 . 우리는 사용 할 수 있습니다 람다 식 우리가 같은 방식의 인수로 기능을 전달할 수 있습니다 :

printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

인터페이스 Predicate대신 표준 기능 인터페이스 를 사용할 수 있으므로 CheckPerson필요한 코드 양이 더욱 줄어 듭니다.


2

익명의 내부 클래스는 다른 객체에 대해 다른 구현을 제공하면서 유리할 수 있습니다. 그러나 프로그램 가독성에 문제가 발생하므로 매우 드물게 사용해야합니다.


1

finalizer guardian 이라고하는 클래스 종료시 익명 클래스의 주요 사용법 중 하나입니다 . Java 세계에서는 finalize 메소드를 사용하여 실제로 필요할 때까지 피해야합니다. super.finalize()수퍼 클래스의 finalize 메소드는 자동으로 호출되지 않으며 메모리 누수에 문제가있을 수 있으므로 서브 클래스에 대한 finalize 메소드를 대체 할 때 항상 호출해야합니다 .

따라서 위에서 언급 한 사실을 고려하면 다음과 같은 익명 클래스를 사용할 수 있습니다.

public class HeavyClass{
    private final Object finalizerGuardian = new Object() {
        @Override
        protected void finalize() throws Throwable{
            //Finalize outer HeavyClass object
        }
    };
}

이 기술을 사용하면 자신과 다른 개발자 가 finalize 메서드가 필요한 super.finalize()각 하위 클래스 를 호출하지 않아도 HeavyClass됩니다.


1

이 방법으로 익명 클래스를 사용할 수 있습니다

TreeSet treeSetObj = new TreeSet(new Comparator()
{
    public int compare(String i1,String i2)
    {
        return i2.compareTo(i1);
    }
});

1

아무도 여기에 언급되지 않은 것처럼 보이지만 익명 클래스를 사용하여 일반 유형 인수를 보유 할 수 있습니다 (일반적으로 유형 삭제로 인해 손실 됨) .

public abstract class TypeHolder<T> {
    private final Type type;

    public TypeReference() {
        // you may do do additional sanity checks here
        final Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public final Type getType() {
        return this.type;
    }
}

익명으로이 클래스를 인스턴스화하면

TypeHolder<List<String>, Map<Ineger, Long>> holder = 
    new TypeHolder<List<String>, Map<Ineger, Long>>() {};

그런 holder인스턴스는 지워지지 않은 전달 된 유형의 정의를 포함합니다.

용법

유효성 검사기 / 탈 직렬화기를 작성하는 데 매우 유용합니다. 또한 리플렉션을 사용하여 일반 유형을 인스턴스화 할 수 있습니다 (따라서 new T()매개 변수화 된 유형을 원한다면 환영합니다!) .

단점 / 제한

  1. 일반 매개 변수를 명시 적으로 전달해야합니다. 그렇지 않으면 형식 매개 변수가 손실됩니다.
  2. 각 인스턴스화는 컴파일러에 의해 생성되는 추가 클래스를 생성하여 클래스 경로 오염 / 항아리 팽창으로 이어집니다.

1

코드를 최적화하는 가장 좋은 방법입니다. 또한 클래스 또는 인터페이스의 재정의 메서드에 사용할 수 있습니다.

import java.util.Scanner;
abstract class AnonymousInner {
    abstract void sum();
}

class AnonymousInnerMain {
    public static void main(String []k){
        Scanner sn = new Scanner(System.in);
        System.out.println("Enter two vlaues");
            int a= Integer.parseInt(sn.nextLine());
            int b= Integer.parseInt(sn.nextLine()); 
        AnonymousInner ac = new AnonymousInner(){
            void sum(){
                int c= a+b;
                System.out.println("Sum of two number is: "+c);
            }
        };
        ac.sum();
    }

}

1

익명 내부 클래스 를 다시 참조되지 않습니다 객체를 생성하는 데 사용됩니다. 이름이 없으며 동일한 명령문으로 선언되고 작성됩니다. 이것은 일반적으로 객체의 변수를 사용하는 곳에서 사용됩니다. 당신은과 변수를 대체 new키워드, 생성자 및 클래스 정의 내부를 호출 {하고 }.

Java로 스레드 프로그램을 작성할 때 일반적으로 다음과 같습니다.

ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();

여기서 ThreadClass사용 된 것은 사용자 정의입니다. 이 클래스는 Runnable스레드를 작성하는 데 필요한 인터페이스를 구현합니다 . 에서 방법 (유일한 방법 )도 구현 될 필요가있다. 제거하는 것이 더 효율적일 것임 이 분명하며 이것이 바로 익명의 내부 클래스가 존재하는 이유입니다.ThreadClassrun()RunnableThreadClass

다음 코드를보십시오

Thread runner = new Thread(new Runnable() {
    public void run() {
        //Thread does it's work here
    }
});
runner.start();

이 코드는 task최상위 예제에서 작성된 참조를 대체합니다 . Thread()생성자 내부의 Anonymous Inner Class는 별도의 클래스를 갖지 않고 Runnable인터페이스 를 구현 하고 run()메서드를 재정의 하는 명명되지 않은 개체를 반환합니다 . 이 메소드 run()에는 스레드에 필요한 작업을 수행하는 명령문이 포함됩니다.

Anonymous Inner Classes가 Java의 장점 중 하나인지에 대한 질문에 대답하면서, 나는 현재 많은 프로그래밍 언어에 익숙하지 않기 때문에 확실하지 않다고 말해야 할 것입니다. 그러나 내가 말할 수있는 것은 확실히 빠르고 쉬운 코딩 방법입니다.

참고 문헌 : Sams는 21 일 7 판에서 Java를 가르칩니다.


0

또 하나의 장점 :
Java가 다중 상속을 지원하지 않는다는 것을 알기 때문에 "Thread"클래스를 익명 클래스로 사용하는 경우 클래스에는 다른 클래스를 확장 할 수있는 공간이 하나 남습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.