JAVA

[JAVA]익명 클래스

당고개 2023. 10. 31. 17:56

1. 익명 클래스

Java에서는 익명 클래스라는 존재가 있습니다. 익명 클래스는 상속 받는 클래스를 명시적으로 별도의 Java 파일을 통해 클래스를 만드는 것이 아닌, 코드 내부에 이름이 존재하지 않는 클래스를 만드는 것입니다.

public class Coffee {
    public void make() {
        System.out.println("Make!!");
    }
}
public class Main {

    public static void main(String[] args) {
 
        // TODO : Coffee 클래스를 상속 받는 익명 클래스
        Coffee coffee = new Coffee() {
            // make 메서드 오버라이드
            public void make() {
                System.out.println("Override Make!!");
            }
            // 새로운 메서드
            public void serve() {
                System.out.println("Serve");
            }
        }
        coffee.make();
        // Override Make!! 

        // serve() 메서드는 호출할 수 없다
        coffee.serve();
        // compile error
    }
}

Coffee 클래스를 상속받는 익명 클래스를 정의 했습니다. 별도의 파일을 이용하는 것이 아니라 코드 내부에서 상속받는 클래스를 재정의 하는 방법입니다. 사실 일반적인 클래스를 익명 클래스로 코드를 작성하는 경우는 거의 없다고 보시면 됩니다. 현업에서는 별도의 파일을 통해 작성합니다.

coffee.make() 메서드와 다르게 coffee.serve() 메서드는 외부에서 호출할 수 없습니다. 그 이유는 new Coffee() 를 통해서 생성하는 인스턴스는 Coffee 클래스가 아닌 Coffee 클래스는 상속받는 익명 클래스이기 때문에 Coffee 클래스 자체에서 serve() 메서드가 선언되어 있지 않기 때문입니다.

다형성의 법칙에 의해 Coffee 클래스에는 serve() 메서드가 존재하지 않습니다. 그러므로 익명 클래스 내에서 호출 하는 것이 아닌 외부 스코프에서 호출할 수 없습니다.

익명 클래스는 왜 존재하는가?

이유는 추상 클래스나, 인터페이스 때문입니다. 추상 클래스나 인터페이스를 Java 파일을 별도로 구현할 수도 있지만 코드 흐름이나 가독성을 위해 가벼운 내용의 경우는 익명 클래스로 작성하는 것이 더 나을 때도 있기 때문입니다.

추상 클래스를 상속 받는 익명 클래스

public abstract class Person {
    abstract public void eat();
    abstract public void sleep();

    public void walk() {
        System.out.println("walk!");
    }
}
public class Main {

    public static void main(String[] args) {
 
        // TODO : Person 클래스를 상속 받는 익명 클래스
        Person person = new Person() {

            public void eat() {
                System.out.println("eat!");
                // walk() 메서드는 Person 클래스의 메서드를 의미한다
                walk();
            }

            public void sleep() {
                System.out.println("sleep!");
            }
        }

        person.eat();
        // eat!
        // walk!

    }
}

Person 클래스를 상속받는 어떤 클래스든, abstract 메서드는 재정의 해야 합니다. 그러므로 익명 클래스를 정의할 때도 abstract 메서드는 반드시 재정의해야 합니다.

코드 상으로는 new Person() 으로 Person 인스턴스를 생성하는 것처럼 보이지만 정확히는 Person 클래스를 상속받는 익명 클래스의 인스턴스를 생성하는 것과 같습니다.

2. 인터페이스를 구현 하는 익명 클래스

익명 클래스의 진정한 진가는 인터페이스 부터 입니다.

public interface Operate {
    int operate(int a, int b);
}
public class Plus implements Operate {
    public int operate(int a, int b) {
        return a + b;
    }
}
public class Calculator {
    private int a;
    private int b;

    public Calculator(int a, int b) {
        this.a = a;
        this.b = b;
    }

    public int result(Operate op) {
        return op.operate(this.a, this.b);
    }
}
public class Main {

    public static void main(String[] args) {
 
        Calculator calculator = new Calculator(20, 10);
        Operate operate = new Plus();
 
        int result = calculator.result(operate);
        // 30

    }
}

Calculator 클래스는 생성자를 통해 두 수를 전달 받습니다. 그리고 result(operate) 메서드를 통해 Operate 인터페이스를 구현하는 인스턴스를 전달합니다. result(operate) 메서드가 할 일은 별 것 없습니다.

전달 받은 Operate 인터페이스의 operate(int a, int b) 메서드를 호출하기만 합니다.
Operate 인터페이스를 구현하는 Plus 클래스는 두 매개 변수를 전달받아 더한 후 결괏값을 반환합니다.

Calculate 클래스의 result(operate) 메서드를 호출하면 Plus 클래스의 operate(int a, int b) 메서드를 호출하고 결괏값을 반환합니다.

public class Minus implements Operate {
    public int operate(int a, int b) {
        return a - b;
    }
}

이번엔 Operate 인터페이스를 구현하고 두 값을 빼는 Minus 라는 클래스를 만들어 봅니다. 그리고 Minus 인스턴스를 생성하여 Calculate 클래스의 result(oprate) 메서드로 전달해봅시다.

public class Main {

    public static void main(String[] args) {
 
        Calculator calculator = new Calculator(20, 10);
        Operate operate = new Minus();
 
        int result = calculator.result(operate);
        // 10

    }
}

이렇게 result(oprate) 메서드에 전달받는 인스턴스에 따라 다른 결괏값을 얻을 수 있습니다.

다만 간단한 내용을 매번 다른 클래스로 정의하여 Java 파일을 만들어서 해야 한다면 귀찮을 수도 있고 불편할 수도 있습니다.

그러나 익명 클래스 방식으로 한다면 Java 파일을 통한 클래스에 대한 정의 필요없이 간단하게 사용할 수 있습니다.

public class Main {

    public static void main(String[] args) {
 
        Calculator calculator = new Calculator(20, 10);

        int result = calculator.result(new Operate() {
            public int operate(int a, int b) {
                return a + b;
            }
        });

        System.out.println(result);
        // 30

        int result2 = calculator.result(new Operate() {
            public int operate(int a, int b) {
                return a - b;
            }
        });

        System.out.println(result2);
        // 10

    }
}

new 키워드를 통해 반환되는 값은 인스턴스의 참조 값이므로 변수를 선언하여 인스턴스의 참조 값을 저장하지 않고 곧 바로 result(operate) 메서드에 전달합니다.

결론적으로 Operate 인터페이스를 상속받는 익명 클래스의 operate(int a, int b) 메서드에 따라 결과 값이 달리집니다.

  1. result(operate) 메서드가 호출될 때, new 키워드를 통해 Operate 인터페이스를 상속 받는 익명 클래스의 인스턴스를 생성 후 참조값을 result(operate) 메서드로 전달합니다.
  2. Operate 익명 클래스 인스턴스를 전달 받은 Calculator 인스턴스는 Operate 인스턴스의 operate(int a, int b) 메서드를 호출합니다.
  3. 익명 클래스에 정의된 operate(int a, int b) 메서드를 실행 후 결과 값을 반환합니다.
  4. 실행흐름은 다시 result(operate) 메서드로 돌아오고 그대로 결과 값을 반환합니다.
  5. 결론적으로 int result 변수에는 30 값이 저장되어 있습니다.

 

 

 

출처 : https://www.codelatte.io/courses/java_programming_basic/O2PZAC2T82LKBXAY

'JAVA' 카테고리의 다른 글

[JAVA]Object 클래스  (0) 2023.11.07
[JAVA]람다 표현식  (2) 2023.10.31
[JAVA]추상클래스와 인터페이스  (0) 2023.10.25
[JAVA]추상화  (0) 2023.10.24
[JAVA]오버라이드(Override)  (0) 2023.10.23