2009년 11월 8일 일요일

java 에서의 call by value 와 call by reference

자바에서는 진정한 call by reference는 없고 reference 값을 넘기는 것만 있다고 들었는데,

그 말을 이해하지 못하다가 이제서야 이해하게 되었다.

 

그 이해를 도와준 포스트가 아래 포스트이다.

 

---------------------------------------------------------------------------------------------

 

테스트 없이 눈으로만 코딩할 때 가장 틀리기 쉬운 부분이 call by value 와 call by refence 를 정확히 이해하지 못할 때 생기는 오류가 아닐까 싶다.

기본형과 객체가 있는데 기본형은 call by value 이고 객체는 call by refence 라는 것은 대부분의 java 개발자들이 알고 있을꺼라 생각한다.

그 예로 swap 함수를 가장 많이 설명하는데..

예제 1)

class Test {
    private static void swap(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
    }

    public static void main(String args[]) {
        int a = 1;
        int b = 2;

        System.out.println("a => " + a);
        System.out.println("b => " + b);

        swap(a, b);

        System.out.println("------- swap 후 -------");

        System.out.println("a => " + a);
        System.out.println("b => " + b);
    }
}

예제 1 의 경우 원하던 결과가 아닌 것을 바로 알아 낼 수 있을 것이다.
swap 메소드에 넘기는 것은 reference 가 아닌 value 이기 때문에...
쉽다. 넘어가자.

다음 예제 2 를 보자
예제 2)

class Test {
    private static void swap(Integer a, Integer b) {
        Integer temp = a;
        a = b;
        b = temp;
    }

    public static void main(String args[]) {
        Integer a = new Integer(1);
        Integer b = new Integer(2);

        System.out.println("a => " + a.intValue());
        System.out.println("b => " + b.intValue());

        swap(a, b);

        System.out.println("------- swap 후 -------");

        System.out.println("a => " + a.intValue());
        System.out.println("b => " + b.intValue());
    }
}

예제 2 의 경우 Integer 는 Object 이다. Object 는 call by reference 다.
따라서 위의 예제는 원하는 결과를 가져올 것이다.
그러나 실행을 하면 예제 1과 전혀 다르지 않다는 것을 알 수 있다.

왜? 객체는 call by reference 라며 사기친거야?

결론부터 말하면 객체는 call by reference 맞다

그러나 해당 객체를 보는 새로운 reference 를 참조해서 넘기는 것이다.

따라서 동일한 객체를 가르키고 있지만
main 에서의 reference 값과 swap 함수에서의 reference 값은 다르다.

따라서 위의 예제의 경우 원하는 결과가 나오지 않는다.

그렇다면 어떻게 해야 해?

예제 3 을 보자.

예제 3)

class Test {
    int value;

    Test(int value) {
        this.value = value;
    }

    private static void swap(Test a, Test b) {
        int temp = a.value;
        a.value = b.value;
        b.value = temp;
    }

    public static void main(String args[]) {
        Test a = new Test(1);
        Test b = new Test(2);

        System.out.println("a => " + a.value);
        System.out.println("b => " + b.value);

        swap(a, b);

        System.out.println("------- swap 후 -------");

        System.out.println("a => " + a.value);
        System.out.println("b => " + b.value);
    }
}

예제 2와 같이 객체의 reference 를 넘긴다.
reference 를 직접 변경하는 게 아니라.
reference 가 참조하는 객체의 value 를 변경하고 있다.
따라서 같은 객체를 보고 있는 main 에서도 값이 바뀌는 것을 알 수 있다.

call by reference

해당 객체의 주소값을 직접 넘기는 게 아닌 객체를 보는 또 다른 주소값을 만들어서 넘기다는 사실을 잊지 말자~

 

 

출처 : java 에서의 call by value 와 call by reference

2009년 11월 4일 수요일

자바 추상클래스와 인터페이스 (abstract class VS interface)

 

다중상속
클래스는 하나의 클래스만을 상속(extends)받을 수 있으므로 추상클래스를 상속받고자 하는 클래스는 한개의 추상클래스만이 상속가능하다. 이에 반해 클래스는 여러개의 인터페이스는 구현(implements)할 수 있으므로 하나의 클래스에 여러개의 인터페이스 구현이 가능하다는 뜻에서 다중상속이 가능하다. (엄격히 말한다면 인터페이는 상속이 아닌 구현이다.)

 

필드 정의
추상클래스에서는 클래스 멤버 변수의 정의가 가능하다.  하지만 인터페이스에서는 멤버변수의 정의를 할 수 없다. 인터페이스에서 멤버변수 선언이 가능하다고 생각해보자. 하나의 클래스에서는 여러 개의 인터페이스를 구현해야하는데 서로 다른 인터페이스에 같은 이름의 멤버변수가 있다면 인터페이스들을 구현한 클래스에서는 그 멤버변수가 어느곳에서 선언한 것인지 구별할 수 있겠는가?! 구별이 안된다.

 

메소드 구현
추상 클래스는 구현메소드와 추상메소드 모두 가능하지만, 인터페이스에서는 추상메소드만이 가능하다. 추상 클래스에서는 메소드에 abstract 키워드를 사용하여 추상 메소드를 표시하고, 인터페이스는 abstract 키워드가 없이 추상메소드를 만든다. 추상 메소드는 접근제한자, 리턴타입,메소드명,전달인자 리스트 로만 구성이 되며, 메소드의 몸체 즉 메소드의 내용을 정의하지 않는다. 추상 메소드를 가지고 있는 추상클래스나 인터페이스를 상속받거나 구현하는 하위 클래스는 상위에 있는 추상 메소드를 모두 구현해야만 한다.

 

상수 정의
상수란 클래스 멤버 변수에 final 키워드를 붙인 것으로 변하지 않는 값을 나타낸다. 하지만 인터페이스에서는 final 키워드를 붙이지 않아도 상수가 된다. 인터페이스에서는 클래스 멤버변수를 가질 수는 없지만 클래스 멤버 상수는 가질 수 있다.

 

 

출처 : 자바 추상클래스와 인터페이스 (abstract class VS interface)