Java에서 익명 클래스의 사용은 무엇입니까? 익명 클래스의 사용이 Java의 장점 중 하나라고 말할 수 있습니까?
Java에서 익명 클래스의 사용은 무엇입니까? 익명 클래스의 사용이 Java의 장점 중 하나라고 말할 수 있습니까?
답변:
"익명 클래스"란 익명의 내부 클래스 를 의미 합니다.
익명의 내부 클래스는 클래스를 실제로 서브 클래스하지 않고도 메소드 재정의와 같은 특정 "엑스트라"를 가진 객체의 인스턴스를 만들 때 유용 할 수 있습니다.
이벤트 리스너를 연결하는 바로 가기로 사용하는 경향이 있습니다.
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// do something
}
});
이 메소드를 사용하면 구현하는 추가 클래스를 만들 필요가 없으므로 코딩이 조금 빨라 ActionListener
집니다. 실제로 별도의 클래스를 만들지 않고도 익명의 내부 클래스를 인스턴스화 할 수 있습니다.
나는이 기술을 전체 수업을 불필요하게하는 "신속하고 더러운"작업에만 사용합니다. 정확히 같은 일을 수행하는 여러 익명의 내부 클래스가 있으면 실제 클래스, 즉 내부 클래스 또는 별도의 클래스로 리팩토링되어야합니다.
overloading methods
하지 overriding methods
?
익명의 내부 클래스는 효과적으로 닫히므로 람다 식 또는 "위임"을 에뮬레이트하는 데 사용할 수 있습니다. 예를 들어 다음 인터페이스를 사용하십시오.
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.
익명의 내부 클래스는 다음 시나리오에서 사용됩니다.
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");
}
});
}
}
때때로 맵 인스턴스화를위한 구문 핵으로 사용합니다.
Map map = new HashMap() {{
put("key", "value");
}};
vs
Map map = new HashMap();
map.put("key", "value");
많은 put 문을 수행 할 때 중복성을 줄입니다. 그러나 외부 클래스를 원격으로 직렬화해야 할 때이 문제가 발생했습니다.
일반적으로 장황한 콜백 형태로 사용됩니다.
나는 그들이 그것을 가지고 있지 않고 매번 명명 된 클래스를 만들어야하는 것에 비해 이점이라고 말할 수 있다고 가정하지만 비슷한 개념은 다른 언어 (클로저 또는 블록)에서 훨씬 잘 구현됩니다.
다음은 스윙 예제입니다
myButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
// do stuff here...
}
});
여전히 장황하지만 장황하지만 모든 상황에서 리스너에 대해 명명 된 클래스를 정의하는 것보다 훨씬 좋습니다 (상황과 재사용에 따라 다르지만 여전히 더 나은 방법 일 수 있습니다)
myButton.addActionListener(e -> { /* do stuff here */})
하거나 더 겁이납니다 myButton.addActionListener(stuff)
.
익명 클래스를위한 가이드 라인.
익명 클래스가 동시에 선언되고 초기화됩니다.
익명 클래스는 하나의 클래스 또는 인터페이스 resp로만 확장하거나 구현해야합니다.
익명 클래스에는 이름이 없으므로 한 번만 사용할 수 있습니다.
예 :
button.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
});
ref.getClass().newInstance()
.
예, 익명의 내부 클래스는 분명히 Java의 장점 중 하나입니다.
익명의 내부 클래스를 사용하면 주변 클래스의 최종 및 멤버 변수에 액세스 할 수 있으며 리스너 등에 유용합니다.
그러나 가장 큰 장점은 주변 클래스 / 방법 / 블록에 밀접하게 연결되어있는 내부 클래스 코드가 특정 컨텍스트 (주변 클래스, 방법 및 블록)를 가지고 있다는 것입니다.
새 스레드를 호출하기 위해 익명 객체를 사용합니다.
new Thread(new Runnable() {
public void run() {
// you code
}
}).start();
내부 클래스는 외부 클래스의 인스턴스와 관련된 두 개의 특별한 종류가 있습니다 : 로컬 클래스와 익명 클래스 . 익명 클래스를 사용하면 클래스를 동시에 선언하고 인스턴스화 할 수 있으므로 코드가 간결 해집니다. 이름이 없기 때문에 로컬 클래스가 한 번만 필요할 때 사용합니다.
클래스 가있는 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
필요한 코드 양이 더욱 줄어 듭니다.
익명의 내부 클래스는 다른 객체에 대해 다른 구현을 제공하면서 유리할 수 있습니다. 그러나 프로그램 가독성에 문제가 발생하므로 매우 드물게 사용해야합니다.
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
됩니다.
아무도 여기에 언급되지 않은 것처럼 보이지만 익명 클래스를 사용하여 일반 유형 인수를 보유 할 수 있습니다 (일반적으로 유형 삭제로 인해 손실 됨) .
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()
매개 변수화 된 유형을 원한다면 환영합니다!) .
코드를 최적화하는 가장 좋은 방법입니다. 또한 클래스 또는 인터페이스의 재정의 메서드에 사용할 수 있습니다.
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();
}
}
익명 내부 클래스 를 다시 참조되지 않습니다 객체를 생성하는 데 사용됩니다. 이름이 없으며 동일한 명령문으로 선언되고 작성됩니다. 이것은 일반적으로 객체의 변수를 사용하는 곳에서 사용됩니다. 당신은과 변수를 대체 new
키워드, 생성자 및 클래스 정의 내부를 호출 {
하고 }
.
Java로 스레드 프로그램을 작성할 때 일반적으로 다음과 같습니다.
ThreadClass task = new ThreadClass();
Thread runner = new Thread(task);
runner.start();
여기서 ThreadClass
사용 된 것은 사용자 정의입니다. 이 클래스는 Runnable
스레드를 작성하는 데 필요한 인터페이스를 구현합니다 . 에서 방법 (유일한 방법 )도 구현 될 필요가있다. 제거하는 것이 더 효율적일 것임 이 분명하며 이것이 바로 익명의 내부 클래스가 존재하는 이유입니다.ThreadClass
run()
Runnable
ThreadClass
다음 코드를보십시오
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를 가르칩니다.