ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Camel][Java] Abstract Class (추상클래스)와 Interface(인터페이스)
    Java/개념 정리 2020. 2. 21. 17:18

    abstract 클래스란?

     abstract 클래스를 해석하면 추상클래스입니다. 추상이라는 말은 한국말이라도 참 어렵죠? 좀더 이해하기 쉽게 해석하면 abstract 클래스는 완전하지 않은 클래스라고 할 수 있습니다. 완전하지 않다는 것은 인스턴스의 생성이 불가능하기 때문이라고 할 수 있습니다. 좀 더 빠른 이해를 돕기 위해 아래의 코드를 가지고 설명하겠습니다. 

    class Animal () {
    	private String kind;
        
        	public void showKind() {
        		System.out.println("Kind : " + kind);
        	}
        
        	public void showBase() {
        	}
    }

    다음과 같이 Animal 클래스를 정의했습니다. 이 Animal 클래스의 showBase 메소드를 확인하면 내용이 비어있는 것을 확인 할 수 있습니다. 그렇다면 내용을 비워둔 이유는 뭘까? 네 그렇습니다. 그 이유은 오직 오버라이딩 관계를 형성하기 위해 생성된 메소드이기 때문입니다. 이런 경우 Animal 클래스를 abstract 클래스로 정의할 수 있습니다. 아래의 코드처럼 말이죠. 

    abstract class Animal () {
    	private String kind;
        
        	public void showKind() {
        		System.out.println("Kind : " + kind);
        	}
        
        	// abstract Method
        	public abstract void showBase() {
        	}
    }

    이처럼 메소드의 내용이 비어있는 것을 확인할 수 있고 완전하지 않은 메소드란 이와 같은 형태의 메소드를 말합니다. 이처럼 하나 이상의 abstract 메소드가 정의되어 있는 클래스는 인스턴스의 생성이 불가능하고 abstract 메소드가 정의되어 있는 클래스는 abstract 클래스가 됩니다.

     즉, abstract메소드가 하나라도 있다면 abstract 클래스로 선언을 해줘야 한다는 것입니다. abstract 메소드가 하나도 없다고 하더라도 인스턴스의 생성을 허용하고 싶지 않다면 abstract 클래스로 선언해주면 됩니다. 

     

     이러한 abstract 클래스는 인스턴스를 생성할 수 없다는 점을 빼면 일반 클래스와 동일합니다. 참조변수의 선언과 메소드 오버라이딩의 원리가 일반 클래스와 마찬가지로 그대로 적용된다는 것입니다. 이러한 abstract 클래스 사용으로 얻을 수 있는 이점은 인스턴스의 생성을 제한함으로서 코드의 안전성을 높일 수 있다는 것입니다. 

     

    abstract 클래스를 상속하는 하위 클래스에서 해주어야 하는 것

    abstract class Animal () { 
    	public void method1() { . . . }
        	public abstract void method2(); 
    }
    
    class Camel extends Animal { // abstract 클래스인 Animal 클래스를 상속하고 있다
    	
        	// abstract 클래스를 상속하고 있기 때문에 Animal 클래스의 abstract 메소드를 오버라이딩 해줘야한다.
        	// 아래의 경우 method2를 오버라이딩 해주지 않았기 때문에 컴파일 오류가 발생한다.
    	public void method3 () { . . . }
    }

    abstract 클래스를 상속하는 하위 클래스라면 반드시 abstract 클래스인 상위 클래스의 abstract 메소드를 오버라이딩 해주어야 합니다. 오버라이딩을 하지 않을 경우 abstract메소드가 그대로 상속되면서 하위 클래스에서 abstract 메소드를 멤버로 가지게 되기 때문입니다. 바로 위에서 설명했죠? 하나 이상의 abstract 메소드를 멤버로 가지게 된다면 클래스를 abstract 선언해야한다는 사실! 

     

     

    interface 란?

    일반적으로 많은 사람들은 interface가 가지는 다중상속을 가능하게 해주는 특성에 주목하는 경향이 있습니다. 물론 interface를 통해 다중상속의 구조를 구현할 수 있는 것은 사실입니다. 그러나 이것은 interface를 만들고 사용하는 궁극적인 이유는 아닙니다.

     interface란 abstract 메소드로만 이루어진 클래스를 말합니다.

    abstract class Animal {
    	public abstract void setKind(String kind);
        	public abstract String findKind(String kind);
    }

     위으 코드를 확인해보면 모든 메소드가 abstract 메소드인 것을 확인 할 수 있습니다. 이럴 경우 우리는 위의 코드를 interface를 사용한 아래의 코드로 표현 할 수 있습니다.

    interface Animal {
    	void setKind(String kind);
        	String findKind(String kind);
    }

    그리고 이러한 interface는 다음의 특징을 지니는 클래스라고 생각할 수 있습니다. 

     

    1. interface 내부의 변수는 public static final로만 선언된다. 

    2. interface 내부의 메소드는 public abstract로만 선언된다.  

     

    1번의 특징을 통해 우리는 interface를 상수표현의 기반으로 활용할 수 있습니다. 기존까지 우리가 알고 있던 상수화시켜 표현하는 방식은 아래의 예시와 같습니다. 

    public class AnimalToNum {
    	public static final int camel = 1;
        public static final int mouse = 2;
        public static final int pig = 3;
        public static final int cow = 4;
    }

    하지만 앞서 제가 interface를 기반으로 상수표현이 가능하다고 했습니다. intercafe 기반의 상수표현의 예시는 아래와 같습니다. 

    public interface AnimalToNum {
    	int camel = 1, mouse = 2, pig = 3, cow = 4;
    }

     훨씬 간결하게 표현이 가능한 것을 확인할 수 있습니다. 하지만 Java에서는 enum이라는 것을 제공하기 때문에 상수 선언이 필요한 상황에서 interface 외에도 enum이라는 것을 사용해야하는 상황이 있을 수 있습니다. 상황에 맞게 사용하시면 됩니다. Java의 enum은 C언어의 enum과는 차이가 있습니다(Java의 enum 더욱 다양한 것을 제공합니다). Java에서의 enum에 대한 설명은 추후의 포스팅에서 다루겠습니다. 

     

     

    interface의 구현

     이러한 특징을 가진 interface는 일반 클래스와 마찬가지로 참조변수의 선언이 가능하고 메소드의 오버라이딩이 그대로 적용됩니다. 이런 interface를 상속하는 것처럼 사용하기 위해서는 다른 클래스에서 상속 할 때 쓰이는 extends 키워드 대신 아래의 코드처럼 implements라는 키워드를 사용합니다. 

    interface Animal {
    	void setKind(String kind);
        	String findKind(String kind);
    }
    
    interface AnotherInterface {
    	void method1(int num);
    }
    
    class Camel implements Animal, AnotherInterface { . . . }

    게다가 한가지 추가적으로, interface는 interface 간에도 상속 관계를 형성하는 것이 가능합니다. interface 간의 상속을 형성할 때는 implements가 아닌 extends 키워드를 사용합니다. 예시는 아래의 코드를 확인하시길 바랍니다. 

    interface Animal {
    	void setKind(String kind);
        	String findKind(String kind);
    }
    
    interface AnotherInterface extends Animal {
    	void method1(int num);
    }
    

     

    Java에서 다중상속을 지원하지않는 이유

     C++의 경우에는 다중상속을 지원합니다. 하지만 Java는 다중상속을 지원하지 않습니다. 그 이유는 다중상속의 장점보단 단점이 더욱 부각된다고 판단하기때문입니다. 그 단적인 예가 바로 다이아몬드 상속입니다. 아래의 코드를 통해 확인해 보겠습니다.

    class Animal{
    	public void act();
    }
    class Camel extends Animal { . . . }
    class Sparrow extends Animal { . . . }
    class AnimalDortor extends Camel , Sparrow {
    	// 여기서 act() 메소드를 호출하면 어떤 일이 벌어질까?
    } 

     이미 언급했다시피 Java에서는 다중상속을 지원하지 않기때문에 컴파일시 에러가 발생하게 됩니다. 그렇다면 다중상속을 지원한다면 아무런 문제가 없을까? Animal 클래스에서 정의한 메소드를 AnimalDoctor 클래스에서 호출하는 경우 문제가 발생하게 됩니다. 그 이유는 AnimalDoctor 클래스가 Animal 클래스를 간접적으로 2번 상속하기 때문입니다. 이런 경우 호출해야할 메소드의 선택에 문제가 생기게되는 것입니다. 이런 이유로 Java에서는 다중상속을 지원하지 않는 것입니다. 

     

    interface를 통한 다중상속의 효과

     Java에서 다중상속을 표현하는 것이 불가능한 것은 아닙니다. interface를 사용하면 다중상속의 효과를 낼 수 있습니다. 하지만 프로그램 구현에 있어서 다중상속은 많이 사용되지 않습니다. 객체지향 시스템의 개발에서 다중상속은 큰 의미르 갖지 못하고, 그렇기 때문에 interface를 다중상속의 일부 지원을 위한 문법으로 이해하는 것은 좋지 않습니다.

     

     

    댓글

Camel`s Tistory.