ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] 추상클래스와 인터페이스
    Language/Java 2019. 11. 9. 16:05

     

     

    오늘은 그동안 저를 참 헷갈리게 해왔던, 추상 클래스(abstract class)와 인터페이스에 대해 정리해보겠습니다.

     

    추상 클래스란?

     

    추상 클래스는 말 그대로 추상적인 클래스를 의미합니다. 잘 와 닿지 않죠?

    변수나 메소드를 가지고 있는 일반적인 형태의 클래스가 바로 실체 클래스입니다. 그리고 추상 클래스는 변수나 메소드가 구체적이지 않은 클래스이고, 그래서 추상 클래스는 어떤 실체 클래스의 공통된 변수나 메소드를 포함하고 있다고 생각할 수 있습니다.

     

    예를 들어 강아지 클래스, 고양이 클래스, 돼지 클래스가 있다고 가정해봅시다. 이 세 클래스가 공통으로 가지고 있는 변수나 메소드에는 어떤 것들이 있을까요? 소리를 낸다는 액션을 공통의 메소드로 표현할 수 있겠죠.

     

    물론 멍멍, 냥냥, 꿀꿀 각 동물들이 내는 소리는 다 다르겠지만 소리를 낸다는 행동 자체는 동일하다고 볼 수 있습니다. 그렇다면 소리를 낸다는 메소드를 가지는 '동물'이라는 추상 클래스를 정의할 수 있습니다. 강아지, 고양이, 돼지 클래스는 동물이라는 추상 클래스를 상속받아 소리를 낸다는 메소드를 각각의 클래스에 맞게 재정의(오버 라이딩)해 사용할 수 있는 것입니다.

     

    public abstract class Animal {
    	public abstract void sound();
    }

    위의 예시에서 동물 클래스(추상 클래스)는 보시는 것과 같이 클래스의 접근 제한자와 'class'사이에 'abstract'를 붙여 표현해줍니다.

    그리고 추상 메소드인 sound() 메소드를 선언합니다. 이때, 추상 메소드는 구현부를 작성하지 않습니다.

     

    public class Dog extends Animal {
    
    	@Override
    	public void sound() {
    		 System.out.println("멍멍");
    		return;
    	}
    	
    }
    public class Cat extends Animal {
    
    	@Override
    	public void sound() {
    		 System.out.println("냥냥");
    		return;
    	}
    }
    public class Pig extends Animal {
    
    	@Override
    	public void sound() {
    		 System.out.println("꿀꿀");
    		return;
    	}
    }

    이렇게 추상 클래스인 Animal클래스를 상속받아 Dog, Cat, Pig 클래스를 구현했습니다.

     

    그렇다면 이러한 추상 클래스를 왜 사용하는 걸까요?

     

    한 마디로 틀을 갖추기 위함이라고 할 수 있습니다. 여러 명의 개발자가 각각 개발을 할 때, 다 다른 구조로 클래스를 설계한다면? 서로 다른 변수명과 메소드명을 사용한다면?

    초기 개발 시 소요되는 시간과 유지보수, 인수인계 시 드는 비용과 노력들이 낭비될 것입니다. 이러한 이유로 개발 초기에 추상 클래스를 만들고, 이를 상속받아 재정의하는 방식으로 개발을 진행하면 훨씬 경제적으로 개발할 수 있습니다.

     

     

    인터페이스(Interface)

     

    인터페이스는 일종의 추상 클래스라고 할 수 있습니다.

    추상 클래스는 자식 클래스로부터 상속받아지기를 기대하며 생성하는 클래스로 자식 클래스에서 재정의할 변수나 메소드 외에도 변수나 메소드가 존재할 수 있습니다. 즉, 위의 예시에서 추상 클래스인 Animal클래스는 추상 메소드 외에도 eat(), walk()와 같은 (abstract가 아닌 구현부가 작성되어 있는) 일반 메소드를 가지고 있을 수 있다는 겁니다.

     

    하지만 인터페이스는 추상 메소드와 상수(final 변수)만을 가질 수 있습니다. 인터페이스에 선언된 모든 메소드는 해당 인터페이스를 구현(implements)한 자식 클래스에 의해 재정의 되어야 하고, 모든 변수들은 상수로 선언되어 초기화 후 값을 변경할 수 없습니다.

     

    하나의 클래스는 여러 개의 인터페이스를 구현(implements)할 수 있고,  구현한 인터페이스에 선언된 모든 추상 메서드들을 재정의해야합니다. 만약 하나라도 재정의하지 않는 경우에는 해당 클래스는 추상 클래스여야 합니다.

    interface A {
    	public abstract void move();
    }
    
    interface B {
    	public void eat();
    }
    
    class C implements A, B {
    	public void move(){
        	System.out.println("move");
        }
        
        public void eat(){
        	System.out.println("eat");
        }
     }

     

    또한 인터페이스 간에도 상속이 가능한데요, 클래스는 하나의 클래스만 상속받을 수 있는 것과 달리 하나의 인터페이스는 여러 개의 인터페이스를 상속받을 수 있습니다.

     

    interface A {
    	public abstract void A();
    }
    
    interface B {
    	public abstract void B();
    }
    
    interface C extends A, B {
    	public abstarct void C();
    }
    
    class CImpl implements C {
    	public void A(){
        	System.out.println("A");
        }
        public void B(){
        	System.out.println("B");
        }
        public void C(){
        	System.out.println("C");
        }
    }
    
    public static void main(String arg[]){
    	A interA = new CImpl();
        interA.A();					//가능
        interA.B();					//불가능
        interA.C();					//불가능
        B interB = new CImpl();
        interB.A();					//불가능
        interB.B();					//가능
        interB.C();					//불가능
        C interC = new CImpl();
        interC.A();					//가능
        interC.B();					//가능
        interC.C();					//가능
    }

     

     

    인터페이스 C가 인터페이스 A와 인터페이스 B를 상속받고, 인터페이스 C의 구현 객체인 클래스가 있다면 이 클래스에서는 인터페이스 A, B, C의 모든 메소드 사용 가능합니다. 단, 상속 관계에서 하위 인터페이스에만 정의되어있는 메소드는 상위 인터페이스 변수로 선언한 경우 호출할 수 없습니다.

     

     

    다음 포스팅에서는 이와 관련하여 다형성에 대한 내용을 다루어 보도록 하겠습니다.

     

     

     

     

Designed by Tistory.