[Java] 추상 클래스와 인터페이스

Geehyun(장지현)·2024년 1월 28일
0

Java

목록 보기
8/12
post-thumbnail

추상 클래스

abstract란 추상적인 이라는 의미를 갖고있다.

  • 추상 메서드 : 미완성 메서드라고도 부르며, {} 중괄호로 작성하는 메서드 바디가 없는 메서드를 말합니다.
    //사용법
    abstract 리턴타입 메서드명(매개변수);
    //사용예시
    abstract void abc();
  • 추상 클래스 : 1개 이상의 추상 메서드를 포함하는 클래슨느 모두 abstract제어자로 추상클래스로 정의해야합니다.
    //사용법
    abstract 클래스명 {...}
    //사용예시
    abstract A {
    	abstract void abc();
       int def(int a) {
       	return a; 
       }
    }

    💡 추상클래스 안에는 모두 추상메서드 뿐일까?
    추상 클래스 내에는 추상 메서드만 있어도, 추상 메서드와 일반 메서드가 둘 다 있어도, 심지어 추상 메서드가 단 하나도 없어도 abstract 제어자로 추상 클래스로 만들 수 있다.
    다만, 추상클래스는 해당 클래스 자체로 객체를 만들지 못한다는 특성이 있어, 추상 메서드가 없는 추상 클래스는 사실상 의미가 없습니다.

추상 클래스의 특징

1. 추상 클래스 자체로는 직접 객체를 생성할 수 없습니다.
2. 추상 클래스를 상속받은 자식 클래스는 상속 받은 모든 추상메서드를 완성(오버라이딩) 해야합니다.

여기서 완성의 기준은 메서드의 {}중괄호 작성여부 입니다. 만약 {} 안에 아무런 내용도 없다고 하더라도, 해당 메서드는 아무런 기능도 하지않는 메서드라고 정의된 완성된 메서드 입니다.

추상 클래스의 객체 생성 방법

1. 일반 클래스로 상속해 객체 생성

abstract class A {
	abstract void abc();
}
class B extends A {
	void abc() {
   	System.out.println("구현완료");
   }
}
//----------------------------------------
public class Main{
	public static void main(String[] agrs) {
   	A aa = new A();          // 불가 : 추상클래스 객체 직접생성 불가
       A ab = new B();          // 가능 : 클래스의 다형성으로 가능
       B bb = new B();          // 가능
   }
}

해당 방법은 추상클래스를 상속받는 클래스를 만들어 해당 클래스로 추상 메서드를 모두 구현완료 후 객체를 생성하는 방법으로, 한번 만들어 두면 별도로 재정의 없이 사용할 수 있다는 장점이 있으나, 일회성으로 사용할 때는 코드가 비효율적으로 길다는 단점이 있습니다.

2. 익명 이너 클래스를 사용

//사용법
클래스명 변수명 = new 생성자() {
	//추상 클래스에 포한된 추상 메서드 오버라이딩 작성
}
//사용예시
abstract class A {
	abstract void abc();
}
//--------------------------------------------
public class Main{
	public static void main(String[] agrs) {
    	A a = new A() {
        	void abc() {
    			System.out.println("구현완료");
    		}
        }
    }
}

위 방법은, A()생성자를 이용해 A객체를 직접 생성하는게 아니라 내부적으로 A클래스를 상속받아 abc()메서드를 오버라이딩하는 익명 클래스의 생성자를 호출하는 개념입니다.
해당 방법은 1회성으로 사용할 때 코드가 짧고 간단하다는 장점이 있으나, 해당 익명 클래스는 한번 사용 후 사라지기 때문에 여러번 사용할 경우 사용할 때마다 계속 다시 작성해줘야한다는 단점이 있습니다.

인터페이스

인터페이스는 클래스와 마찬가지로 Java 내에서 객체지향 프로그래밍 요소 중 하나입니다.

//사용법
interface 인터페이스명 {
	public static final 자료형 필드명 =;  //인터페이스 내 필드 작성법
    public abstract 리턴타입 메서드명();     //인터페이스 내 메서드 작성법
}

인터 페이스 내에서는 아래와 같이 필드, 메서드에 대한 제어자가 한정 됩니다.

  • 필드 : public static final로 제어자가 한정 됩니다.
  • 메서드 : public abstract로 제어자가 한정 됩니다.

작성할 때 위 제어자를 입력하지 않더라도 컴파일러가 자동으로 해당 제어자를 모두 추가합니다.

인터페이스의 상속

//인터페이스만 상속
클래스명 implements 인터페이스명, 인터페이스명 {
	...
}
//인터페이스와 클래스 둘다 상속
클래스명 extends 부모클래스명 implements 인터페이스명, 인터페이스명 {
	...
}
  1. implements라는 키워드를 이용해서 해당 인터페이스를 상속
  2. 클래스와는 달리 다중 상속이 가능 implements 인터페이스명, 인터페이스명으로 , 쉼표로 구분하여 작성 (몇개든 상속 가능)
  3. 인터페이스와 클래스 둘다 상속 가능하며, 작성 시 클래스명 extends 부모클래스명 implements 인터페이스명으로 순서를 반드시 지켜서 작성해야한다.

💡 왜 클래스는 다중 상속이 불가한데, 인터페이스는 가능할까?
클래스가 다중 상속이 불가했던 이유는 여러 클래스를 상속 받게될 경우 부모 클래스끼리 변수명, 메서드 명등의 중복이 발생하게 될 경우 충돌이 발생하게 되기 때문이었다.
하지만 인터페이스의 경우 모든 필드가 public static final 으로 저장 공간이 별개로 나눠져있고, 모든 메서드가 public abstract으로 모두 미완성 메서드로 자식클래스에서 완성해서 사용하기 때문에 다중 상속이 불가할 이유가 없어졌기 때문입니다.

인터페이스의 객체 생성 방법

인터페이스 또한 추상 메서드를 사용하기 때문에 추상클래스와 마찬기지로 직접 객체를 생성할 수가 없습니다.

1. 일반 클래스로 상속해 객체 생성

interface A {
	public abstract void abc();
}
class B implements A {
	public void abc() {    //인터페이스에서는 제어자가 한정되어있다는 점을 명심해야함
   		System.out.println("구현완료");
   }
}
//----------------------------------------
public class Main{
	public static void main(String[] agrs) {
   		A aa = new A();          // 불가 : 인터페이스 객체 직접생성 불가
    	A ab = new B();          // 가능 : 클래스의 다형성으로 가능
    	B bb = new B();          // 가능
   }
}

해당 방법은 한번 만들어 두면 별도로 재정의 없이 사용할 수 있다는 장점이 있으나, 일회성으로 사용할 때는 코드가 비효율적으로 길다는 단점이 있습니다.

2. 익명 이너 클래스를 사용

interface A {
	public abstract void abc();
}
//----------------------------------------
public class Main{
	public static void main(String[] agrs) {
   		A a = new A() {
        	public void abc() {    //인터페이스에서는 제어자가 한정되어있다는 점을 명심해야함
   				System.out.println("구현완료");
  			}
        }
	}
}

해당 방법은 1회성으로 사용할 때 코드가 짧고 간단하다는 장점이 있으나, 해당 익명 클래스는 한번 사용 후 사라지기 때문에 여러번 사용할 경우 사용할 때마다 계속 다시 작성해줘야한다는 단점이 있습니다.

💡 인터페이스를 상속할 때 주의점
앞서 인터페이스는 필드와 메서드에 제어자가 한정되어있다 정리했습니다. 또한, 인터페이스 내 필드와 메서드에 제어자를 입력하지 않을 경우 컴파일러가 자동으로 정해진 제어자를 입력하기 때문에 상속할 때 인터페이스 내 제어자가 없더라도 필드는 public static final, 메서드는 public abstract 제어자가 작성되어있다는 점을 명심해야합니다.

interface A {
	void abc();              //public abstract 제어자가 자동으로 포함되어있음
    int a = 10;              //public static final 제어자가 자동으로 포함되어있음
}
class B implements A {
	void abc() {...}         //제어자가 자동으로 default로 들어가서 에러 남
    public abc() {...}       //정상
    int a = 5;               //final 필드라 값 변경 불가
}

인터페이스 내 default 메서드

자바 8에서 등장한 개념으로 인터페이스 내에 default 메서드가 포함될 수 있어졌다.
물론 여전히 인퍼페이스 내 제어자를 생략 시 자동으로 public abstract 제어자가 삽입되므로 해당 default는 인터페이스 내에서만 사용되는 개념입니다.

추가된 이유

기존 인터페이스 내 모든 메서드는 abtstract 제어자를 포함하기 때문에 해당 인터페이스를 상속받은 모든 클래스는 해당 메서드를 모두 완성해야합니다.
이는 추후 해당 인터페이스에 메서드를 추가하게될 경우 기존 해당 인터페이스를 상속 받은 모든 클래스에서 오류가 나게 됩니다. (추가된 추상 메서드를 완성하지 않았기 때문)

따라서 인터페이스에도 default메서드를 포함시킬 수 있도록 추가되었습니다.
해당 default메서드는 구현이 필수가 아니며 자식 클래스에서 오버라이딩도 가능합니다.

오버라이딩 할 때는 default가 아니라 public으로 구현해야합니다. (default는 인터페이스 내에서만 해당되기 때문)

상속받은 인터페이스 내 메서드 사용법

상속받은 인터페이스의 메서드(default 메서드) 사용시에도 super키워드를 사용합니다.
다만, 그냥 super.메서드명();으로 사용하게될 경우 인터페이스가 아니라 해당 클래스가 상속받은 부모 클래스 또는 최상위 클래스인 Object 에서 메서드를 호출하게 됩니다.
따라서, 상속받은 인터페이스에서 메서드를 찾는다는걸 정확히 작성해야합니다.

interface A {
	default void abc() {...}
}
class B implements A {
	void def() {
    	super.abc();      //부모 클래스 또는 Object 클래스에서 메서드 호출
        A.super.abc();    //super 앞에 인터페이스명 기입
    }
}

인터페이스 내 static 메서드

자바 8이후에 추가된 기능으로 인터페이스 내 static메서드를 작성할 수 있게 되었습니다.

interface A {
	static void abc() {...}
}

참고

Do it! 진짜 개발자가 되는 Java 프로그램 입분서 자바 완전 정복 - 김동형
위 책을 공부하며 작성하고 있습니다!

profile
개발자를 꿈꾸는 병아리 (블로그 이전 준비중 입니다.)

0개의 댓글