본문 바로가기

Programming/JAVA

C함수 포인터와 자바의 Class&Interface

아래 내용은 effective java라는 책의 내용을 정리한 것 입니다.

C언어에서는 함수 포인터란 것을 제공하여 C프로그램에서 특정 함수를 호출할 수 있게 하며 또한 전달도 가능하게 하고 있습니다. (C#에서는 대리자인 delegate를 제공하죠^^)

함수 포인터는 일반적으로 함수 호출자가 이차적인 함수에 포인터를 전달함으로써 해당 함수를 처리할 수 있도록 하는데 이 작업을 종종 콜백(Callback) 이라고 부르기도 합니다.

예를 들어 C 표준라이브러리의 qsort 함수는 comparator 함수에 함수 포인터를 전달하며 포인터로 넘어오는 함수에서 정렬을 원하는 요소(Element)를 비교하여 소트를 하게 되는 것 입니다.

이러한 함수 포인터를 자바에서는 객체 참조를 통해 구현이 가능하므로 Java에서는 생략 되었는데 객체에 대한 메소드를 호출하는 것은 일반적으로 해당 객체에 처리작업을 맡기는 것인데 다른 객체를 대상으로 처리작업을 수행하는 메소드를 갖는 객체를 정의할 수도 있습니다. 그러한 메소드를 정확히 외부로 전달하는 클래스의 인스턴스는 실제 포인터 역할을 하는데 이러한 인스턴스를 함수 객체라고 합니다.

다음의 코드를 보죠~

class StringLengthComparator{
    public int compare(String s1, Stirng s2) {
        Return s1.length() – s2.length();
    }
}

위 클래스는 두개의 문자열을 취해 첫번째 것이 짧으면 음수, 같으면 0, 길면 양수를 리턴하는 메소드인 compare를 가지고 있습니다. 여기서 StringLengthComparator의 인스턴스인가 함수 포인터의 역할을 하는 것입니다.

Concret Strategy 클래스에서는 일반적이지만 StringLengthComparator 클래스는 소속이 정해져 있지 않습니다. 또한 불필요한 객체 생성을 억제하기 위해 Singleton으로 정의가 가능한데 …

다음의 예를 보죠~

class StringLengthComparator {
    private StringLengthComparator() {}
   
    public static final StringLengthComparator INSTANCE = new StringLengthComparator();

    public int compare(String s1, String s2) {
        Return s1.length() – s2.length();
}
}

StringLengthComparator 인스턴스를 메소드에 전달하려면 파라미터에 대한 적절한 타입이 필요한데 Comparator 인터페이스를 정의하고 StringLengthComparator 클래스가 인터페이스를 구현하는 것으로 정의하는 것도 가능 합니다.

//Strategy Interface
public interface Comparator {
    public int compare(Object o1, Object o2);
}

위 내용은 java.util 패키지에 있는 Comparator 인터페이스의 정의인데 문자열 이외의 객체에 해당하는 comparator가 되기 위해서는 StringLengthComparator 클래스가 compare를 구현하기 위해 조금 수정되어야 하는데 Object 파라미터는 length 메소드를 호출하기 전에 String으로 Casting 되어야 합니다.

Concrete Strategy(StringLengthComparator) 클래스는 한번만 사용되어 지는 경우엔 종종 익명 클래스(Anonymous Class)로써 정의되는데 다음을 보도록 합니다.

Arrays.sort(stringArray, new Comparator() {
    public int compare(Object 01, Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
 
        return s1.length() – s2.length();
    }
});


Strategy 인터페이스가 이를 구현한 모든 Concrete Strategy 클래스의 인스턴스에 타입의 역할을 수행하므로 Concrete Strategy 클래스는 public으로 설정될 필요가 없으며 대신 “호스트 클래스(host class)”가  Strategy 인터페이스를 타입으로 갖는 public static 필드를 전달할 수 있으며 Concrete Strategy 클래스는 해당하는 호스트 클래스에 대해 private 중첩 클래스가 될 수 있습니다.

아래 예제에서 static 멤버 클래스는 Concrete Strategy 클래스가 Serializable 이라는 두 번째 인터페이스를 구현하게 하도록 Anonymous 클래스에 선행되어 사용되고 있습니다.

// Concrete Strategy 내용의 전달
class host {
    ……
    private static class StrLenCmp implements Comparator, Serializable {
        public int compare(Object o1, Object o2) {
String s1 = (String)o1;
            String s2 = (String)o2;
 
            return s1.length() – s2.length();
        }
    }

    public static final Comparator STRING_LENGTH_COMPARATOR = new StrLenCmp();
}


요약하면 C 함수 포인터의 주된 용도는 Strategy 패턴을 구현하기 위한 것인데 자바에서는 이를 위해 전략 내용과 각각의 Concrete Strategy을 대상으로 하는 인터페이스를 구현하는 클래스를 나타내는 인터페이스를 선언 합니다.

구체적인 전략이 한번만 사용되는 경우에는 익명 클래스를 이용하여 구현되면 되고 Concrete Strategy가 반복적으로 전달되는 경우에는 바로 위의 예문처럼 Strategy 인터페이스를 타입으로 하는 public static 필드를 사용하시면 됩니다.