📁 바인딩 Binding
프로그램에 사용된 구성 요소의 실제 값 또는 프로퍼티를 결정짓는 행위를 의미한다.
메서드에서 바인딩이란 메서드를 사용할 때 호출된 메서드와 실제 메서드가 위치한 메모리를 연결하는 행위이다.
오버라이딩을 예시로 들자면 조상 메서드와 자손에서 오버라이딩된 메서드 중 어떤 것을 사용할지 결정하는 과정이다.
바인딩은 그것이 수행되는 시점에 따라 정적바인딩과 동적바인딩으로 구분된다.
바인딩이 컴파일타임에 수행되면 정적바인딩, 런타임에 수행되면 동적바인딩이다.
📁 정적바인딩 Static binding
자바 컴파일러는 실제 인스턴스의 타입 정보를 정확히 알지 못하는 채로 컴파일을 진행한다.
인스턴스는 런타임이 되어서야 new 연산자에 의해 메모리에 로드되기 때문이다.
따라서 컴파일타임에 바인딩의 기준이 될 수 있는 정보는 클래스 데이터나 메서드 정보, 참조변수의 타입 정도이다.
원칙적으로 모든 메서드는 컴파일타임에 메모리에 고정적으로 할당되어 프로그램이 종료될 때까지 유지된다.
단, 실제 메서드를 사용한 인스턴스에 의존해야 하는 특정 메서드의 경우 동적바인딩이 정적바인딩보다 우선시될 수 있다.
static 메서드
static 메서드의 경우 원칙적으로 클래스의 이름을 통해 호출하기 때문에 인스턴스의 타입 정보가 필요하지 않다.
설령 인스턴스의 참조변수를 통해 참조한다고 하더라도 중요한건 참조변수의 타입이지 인스턴스가 아니다.
따라서 컴파일타임에 어떤 클래스의 메서드를 사용할지 결정하는 정적 바인딩이 가능하다.
오버로딩
오버로딩된 메서드의 경우에도 정적바인딩이 가능하다.
메서드 선언부의 매개변수 정보와 메서드에 들어온 인자를 비교하여 적절한 메서드를 선택할 수 있기 때문이다.
private, final 메서드
private이나 final이 붙은 메서드는 상속이 불가능해 메서드를 호출한 참조변수 타입의 클래스에만 존재할 수 있다.
따라서 static 메서드와 마찬가지로 컴파일러 선에서 클래스 정보만으로 바인딩이 가능하다.
📁 동적바인딩 Dynamic binding
컴파일러는 참조변수가 참조하는 인스턴스의 실제 타입을 모르기 때문에 참조변수 타입의 클래스에 메서드가 존재하는지를 기준으로 컴파일을 진행하였다.
그러나 프로그램이 실행되면 JVM은 참조변수가 참조하고 있는 인스턴스의 정확한 타입을 체크한다.
따라서 실제 객체의 타입을 기준으로 메서드를 호출하는 동적바인딩이 가능해진다.
오버라이딩
인스턴스 메서드는 메서드를 사용하는 인스턴스의 타입에 따라 어떤 클래스의 메서드가 호출될지 결정된다.
오버라이딩이 된 경우라면, 조상타입의 객체로 호출했을 땐 조상클래스의 메서드가, 자손타입 객체로 호출했을 땐 자손클래스의 메서드가 사용되어야 할 것이다.
따라서 인스턴스의 타입이 불분명한 컴파일타임에는 어떤 메서드를 호출해야 할지 확신할 수 없고 실제 인스턴스의 타입이 드러나는 런타임에 와서야 동적으로 바인딩되는 것이다.
📁 참조변수의 다형성
이처럼 런타임에 와서야 실제 인스턴스의 타입을 알 수 있는 것은 자바의 다형성 때문이다.
자바는 조상타입 참조변수로 자손클래스의 객체를 참조할 수 있도록 함으로써 다형성을 프로그래밍적으로 구현하였다.
컴파일러가 객체의 실제 타입을 모르는 채로 컴파일하도록 한 것 역시 다형성을 실현하려는 의도가 담겨 있다.
조상타입 참조변수 p로 자손 객체를 참조하고 자손클래스에서 오버라이딩된 메서드 method()를 호출하였다.
Parent p = new Child();
p.method();
여기서 컴파일러는 p가 참조하고 있는 인스턴스의 실제 타입을 확신할 수 없다.
다형성에 의해 참조변수 타입인 Parent가 아니라 그 자손의 객체일 가능성도 존재하기 때문이다.
하지만 p가 Parent이건 Child이건 method()를 멤버로 가지고 있다는 점은 분명하기 때문에, 에러를 발생시키지 않고 컴파일하는 것이다.
이처럼 다형성은 참조변수의 타입과 인스턴스의 타입이 항상 일치하지 않을 수 있다는 점을 환기한다.
그래서 일단 참조변수 타입에 메서드가 존재하는지를 기준으로 컴파일을 하고, 런타임에서 인스턴스의 타입 정보를 받아 새롭게 바인딩하는 것이다.