많은 프로그래머가 코딩 중 'Non static method cannot be referenced from a static context'(정적 컨텍스트에서 비정적 메서드를 참조할 수 없습니다)라는 오류 메시지를 마주합니다. 이 오류는 특정 IDE에 국한된 문제가 아니라, 오류 발생 조건이 충족되면 어떤 개발 환경에서든 나타날 수 있습니다.
이는 초보자에게 매우 흔한 실수로, 클래스의 인스턴스를 생성하지 않은 채 클래스를 '정적으로' 사용하려 할 때 발생합니다. 정적(static) 클래스를 사용할 때는 반드시 지켜야 할 몇 가지 조건이 있습니다. 이 글에서는 다양한 사례를 살펴보고 정적 클래스를 올바르게 사용하는 방법을 안내해 드립니다.
정적 메서드(Static Method)란 무엇인가?
메서드에 'static' 키워드를 붙이면 해당 메서드는 정적 메서드가 됩니다. 일반적인 메서드가 객체에 속하는 것과 달리, 정적 메서드는 객체가 아닌 클래스 자체에 속합니다. 따라서 클래스의 인스턴스를 생성하지 않고도 정적 메서드를 손쉽게 호출할 수 있습니다.
정적 메서드는 여러 용도로 활용됩니다. 예를 들어 정적 데이터 멤버와 그 값을 변경할 때 사용할 수 있습니다. 하지만 정적 메서드에는 몇 가지 제약도 존재합니다. 예를 들어 클래스의 비정적(non-static) 필드에 접근하려면 반드시 비정적 메서드를 사용해야 합니다. 요약하자면, 정적 메서드는 아주 제한적으로 사용되지만 분명한 장점을 가지고 있습니다.
다음은 정적 메서드를 사용해 모든 객체의 속성을 한 번에 변경하는 간단한 예제입니다.
class Students{
int roll_no;
String name;
static String college = "InformationTech";
static void change(){
college = "Management"; }
Students (int number, String name_self){
roll_no = number;
name = name_self; }
void display (){System.out.println(rollno+" "+name+" "+college);}
public static void main(String args[]){
Students.change();
Students stu_1 = new Students (100,"Mexican");
Students stu_2 = new Students (202,"American");
Students stu_3 = new Students (309,"British");
stu_1.display();
stu_2.display();
stu_3.display();
} }이 프로그램의 실행 결과는 다음과 같습니다.
100 Mexican Management
202 American Management
309 British Management
클래스와 클래스 인스턴스의 차이점은 무엇인가?
길을 걷다가 자동차를 봤다고 생각해 보세요. 모델이나 종류를 모르더라도 그것이 '자동차'라는 것은 즉시 알 수 있습니다. 이는 이미 알고 있는 '자동차'라는 클래스에 속한다는 것을 직관적으로 파악하기 때문입니다. 여기서 클래스는 하나의 템플릿 또는 개념이라고 생각하면 됩니다.
이제 조금 더 가까이 다가가면 자동차의 모델과 제조사를 알 수 있습니다. 이 순간 여러분은 '자동차' 클래스의 인스턴스(instance)를 인식하게 되는 것입니다. 인스턴스에는 바퀴, 마력, 휠 등 모든 속성이 구체적으로 담겨 있습니다.
속성의 예를 들어보면, '자동차' 클래스는 모든 자동차가 바퀴를 가져야 한다고 규정합니다. 그리고 여러분이 보고 있는 실제 자동차(자동차 클래스의 인스턴스)는 알로이 휠을 장착하고 있는 식입니다.
객체 지향 프로그래밍에서는 클래스를 직접 정의하고, 그 안에 'color' 같은 타입의 필드를 선언합니다. 클래스가 인스턴스화될 때마다 백그라운드에서 color 필드를 위한 메모리가 자동으로 할당되며, 이후 여러분은 이 필드에 특정 값(예: 빨강)을 지정할 수 있습니다. 이처럼 개별 객체마다 고유한 속성들은 비정적(non-static)입니다.
반면 정적 메서드와 정적 필드는 모든 인스턴스가 공유합니다. 이들은 인스턴스가 아닌 클래스 자체에 고유한 값이나 항목을 위해 만들어집니다. 메서드의 경우 전역 메서드(예: 문자열을 정수로 변환하는 변환기)가 될 수 있고, 필드의 경우 코드상에서 상수인 경우가 많습니다(예: 일반 자동차만 생산한다면 자동차 타입을 정적으로 선언할 수 있습니다).
이제 코딩 시 오류가 발생할 수 있는 다양한 경우와 그 해결 방법을 살펴보겠습니다.
문제 1: 존재하지 않는 것을 호출하고 있는 경우
실제로 정적 메서드와 비정적 메서드를 함께 사용하다가 오류가 발생한 사례를 접할 수 있습니다. 이럴 때는 어떤 메서드가 무엇을 호출하는지(정적인지 아닌지 관점에서) 주의 깊게 확인해야 합니다. 다음 코드를 살펴보세요.
private java.util.List<String> someMethod(){
/* Some Code */
return someList; }
public static void main(String[] strArgs){
// The following statement causes the error. You know why..
java.util.List<String> someList = someMethod(); }여기서 정적 메서드(main)가 someMethod를 호출하고 있습니다. 객체 지향 프로그래밍에서는 데이터를 연산 대상 데이터와 함께 캡슐화합니다. 객체가 없으면 인스턴스 데이터도 존재하지 않으며, 인스턴스 메서드는 클래스 정의의 일부로 존재하더라도 실제로 동작하려면 항상 객체 인스턴스가 데이터를 제공해 주어야 합니다.
요약하면, 존재하지 않는 것은 호출할 수 없습니다. 객체를 생성하지 않았다면 비정적 메서드는 아직 존재하지 않는 것입니다. 반면 정적 메서드는 정의상 언제나 존재합니다.
문제 2: 메서드가 정적으로 선언되지 않은 경우
정적 main 메서드에서 인스턴스를 생성하지 않은 채 다른 메서드를 호출하면 오류 메시지가 나타납니다. 여기에도 동일한 원칙이 적용됩니다. 존재하지 않는 것에는 접근할 수 없다는 것입니다.
public class BookStoreApp2 {
//constants for options
Scanner input = new Scanner (System.in);
public static void main(String[] args) {
BookStoreItem[] item;//declaring array
item = new BookStoreItem[10];//initializing array
int itemType = -1;
printMenu();
getUserChoice();
for (int i = 0; i < item.length; i++){
}//end of switch statement
}//end of for loop
for (int i = 0; i < item.length; i++) {
}//end of for
}//end of main method위 코드에서는 printMenu()와 getUserChoice() 두 메서드를 모두 정적 메서드로 변환해야 합니다.
이런 상황을 우회하고 싶다면 대신 생성자(constructor)를 활용할 수 있습니다. 예를 들어 main() 메서드의 내용을 생성자 안으로 옮기는 방법입니다.
public BookStoreApp2()
{
// Put contents of main method here}
이렇게 한 후 main() 메서드 안에서 다음과 같이 작성합니다.
public void main( String[] args )
{
new BookStoreApp2(); }
위 팁으로 해결되지 않거나 코드 구조가 다르다면, 정적 클래스와 정적 메서드의 기본 원칙을 다시 떠올리고 코드를 재검토하여 기본 원칙이 위반되지 않았는지 확인해 보시기 바랍니다.