본문 바로가기

공부/컴퓨터

.class file format 과 overriding에 관한 참고 사항

반응형

다음의 내용을 가지고 테스트를 해 보자.

  1. public class A {
  2.     public String getString() {
  3.         return "call A.getString() Method";
  4.     }
  5. }

  1. public class B extends A {
  2.     public String getString() {
  3.         return "call B.getString() Method";
  4.     }
  5. }

  1. public class C {
  2.     public static void main(String... strs) {
  3.         B b = new B();
  4.         System.out.println ( b.getString() ) ;
  5.     }
  6. }


이렇게 만든 상태에서 모두 컴파일을 하고, C 화일을 실행 시키면  "call B.getString() Method" 가 나온다.


 그 후에 B.java 화일은 그대로 두고 A 클래스에 메소드를 하나 추가 하여 다시 작성하자.

  1. public class A {
  2.     public String getString() {
  3.         return "call A.getString() Method";
  4.     }
  5.     // 아래의 메소드를 추가 한다.
  6.     public String getString2() {

            return "call A.getString2() Method";

        }  

  7. }


그리고 C 화일을 수행 부분인 main 메소드를 다음과 같이 변경 시킨다.

  1. public class C {
  2.     public static void main(String... strs) {
  3.         B b = new B();
  4.         System.out.println( b.getString2() );
  5.     }
  6. }


그리고 A 와 C 만을 컴파일 하고 ( B 는 꼭 그대로 둔다. ), C 클래스를 수행하면 어떤 결과가 날까?

( B class 는 아직까지 컴파일 되지 않았고, B의 부모인 A 만 새로운 메소드를 추가 하고 새롭게 컴파일 되었다. )


결과는 다음과 같다. call A.getString2() Method


B클래스는 전혀 새롭게 컴파일 하지 않았는데, 어떻게 A클래스에 있는 메소드를 알아서 호출해 주는것일까?


물론 저렇게 만들지 않으면 정말로 불편하게 된다. 상위클래스에서 자식으로 넘겨 줄 수 있는 제한자를 가지고 있는 것들을 추가 할때마다 자식클래스들도 모두 다시 컴파일 해야 한다면 얼마나 큰 낭비인가? 하지만 그렇게 상식적으로 생각하지 말자. 잘못된 상식은 수두룩하니깐.


그렇게 해서 내가 테스트 해 본 방법은 다음과 같다.


  • 직접 수행 해 보기. - 이미 위의 방법을 통해서 확인해 보았다.
  • 원래의 B.class 화일과 새롭게 컴파일 된 B.class 화일을 바이너리 비교
    • 비교해 보았으나 두개의 바이너리 내용이 똑 같음.


이렇게 한다고 해도, 이것은 테스트 밖에 되지 않는다. 우연찮게 두개의 메소드 테이블이 같아서 똑같은 바이너라가 나올 수도 있다고 생각 할 수도 있다. 그러므로 좀 더 정확한 답을 찾아 보도록 하자.


.class 화일 Format 를 직접 찾아 보자.

 The class File Format : http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html


 class 화일은 다음과 같은 구조로 이루어져 있다.

    ClassFile {
     u4 magic;
     u2 minor_version;
     u2 major_version;
     u2 constant_pool_count;
     cp_info constant_pool[constant_pool_count-1];
     u2 access_flags;
     u2 this_class;
     u2 super_class;
     u2 interfaces_count;
     u2 interfaces[interfaces_count];
     u2 fields_count;
     field_info fields[fields_count];
    u2 methods_count;
   
method_info methods[methods_count];
     u2 attributes_count;
     attribute_info attributes[attributes_count];
    }


그 중에서 우리가 관심이 있는 부분은 메소드이고, 그것과 관련 있는 부분은 아래의 두개이다.

u2 method_count;

method_info methods[methods_count];


우선 method_count 에 대한 설명을 보자.

methods_count
The value of the methods_count item gives the number of method_info structures in the methods table.

methods_count 는 methods 테이블에 있는 method_info 갯수란다. - 별로 중요하지 않군. 흠.


methods[]
Each value in the methods table must be a method_info (§4.6) structure giving a complete description of a method in this class or interface. If the method is not native or abstract, the Java virtual machine instructions implementing the method are also supplied.

The method_info structures represent all methods declared by this class or interface type, including instance methods, class (static) methods, instance initialization methods (§3.9), and any class or interface initialization method (§3.9). The methods table does not include items representing methods that are inherited from superclasses or superinterfaces.


우리가 원하는 내용을 찾아 버렸다. .class 화일에서는 methods[] 이 있고, 이 안에는 method_info가 들어 있는데, 이 method_info에는, 현재 클래스나 인터페이스에서 선언된 모든 메소드들이 들어 있다고 한다. 즉, 부모에 선언되어 있거나 하는 메소드들은 현재의 .class 화일에서 가지고 있지 않다는 말이 된다.


== 추가한 부분 ==
B.class 화일에서는 자기에게서 선언되지 않은, getString2() 라는 메소드 정보를 class 화일 안에 가지고 있지 않다.
( 왜냐하면 위에서 말했다 시피 .class 화일 안에는 자신에게서 선언된 메소드 정보만을 가지고 있어야 하기 때문이다. )

C 클래스에서 B 객체의 getString2() 메소드를 호출하게 되면,
B 객체에서는 자신의 메소드테이블을 검색해서 getString2() 가 있는지 확인하게 된다.

하지만 B 객체에서는 getString2() 이라는 메소드가 존재하지 않기 때문에,
부모인 A 객체에게 getString2() 을 요청하게 될 것이다.

자식 클래스인 B에서 선언되지 않았지만, 부모 클래스인 A 에서 메소드를 추가하여도,
C 클래스에서 A에서만 추가된 메소드를 B 에게 호출 하여도 문제가 없게 된다.

즉, B 클래스는 전혀 수정없이 확장성 있게 코드를 작성할 수 있게 된다.



그렇기 때문에,  위의 예에서 B.java 화일은 새롭게 컴파일 하지 않아도 정상적으로 동작하는게 옳다는 결론이 나온다.

반응형