추상 클래스 (Abstract Class)
추상 클래스는 일반 클래스와 비교해서 크게 다른 점은 없다. 단지 추상 클래스에 추상 메서드를 작성하여 상속받는 자식 클래스에서 메서드를 완성하도록 유도하는 클래스이다. 그래서 추상 클래스를 '미완성 클래스'라고 한다.
추상 클래스를 선언하는 방법은 class 앞에 abstract라는 키워드를 붙여주면 된다.
Ex)
public abstract class abstractClassEx
인터페이스 (Interface)
인터페이스는 일반 클래스와 비슷한 구조를 보여준다. 추상 클래스는 '미완성 설계도'라고 한다면 인터페이스는 '기본 설계도'라고 할 수 있다.
인터페이스를 선언하는 방법은 class 대신 interface를 작성해주면 된다.
Ex)
public interface interfaceEx {
String name = "홍길동"; // 변수 선언시 기본적으로 public static final을 컴파일러가 붙이고 수행해줌.
void method1(); // 아무것도 붙이지 않으면 컴파일러가 public abstract를 붙이고 수행함.
}
공통점
- new 키워드를 사용하여 객체로 만들 수 없다.
- 상속받는 자식 클래스는 반드시 추상 클래스나 인터페이스를 구현해야 한다.
차이점
인터페이스 | 추상 클래스 |
다중 상속 가능. | 다중 상속 불가능. |
추상 메소드만 가능.(자바 8 이후 default 와 static 으로 일반 메서드 선언이 가능.) | 일반 메서드와 추상 메서드 가능. |
상속받는 클래스는 반드시 인터페이스에 있는 모든 메서드를 구현해야 한다. | 상속 받은 클래스는 추상 클래스에 있는 추상 메서드만 구현해도 됨. |
implements 사용 | extends 사용 |
이렇게 놓고 보니 추상 클래스와 인터페이스로 나눠서 사용하는 이유가 뭘까?
그 이유를 필자는 다중 상속 때문이라고 본다. 앞서 말했듯이 추상 클래스는 다중 상속이 불가능하다. 그래서 인터페이스는 다중 상속이 가능하도록 만들어졌다. 그러면 코드를 통해 필자가 다중 상속으로 인해 인터페이스와 추상 클래스로 나눠서 사용했다고 했는지 보도록 하자!
Animal.java
public abstract class Animal {
String weapon;
String name;
void getName(){
System.out.println("나는" + name + "입니다.");
};
void attack(){
System.out.println(weapon + "을 사용해서 공격.");
};
}
Animal이라는 추상 클래스를 하나 만들었다. 이 클래스를 상속받아 Human, Bird 클래스를 만들었다.
Human.java
public class Human extends Animal implements Talkable {
@Override
public void talk() {
System.out.println("사람은 말을 할 수 있다.");
}
@Override
void getName() {
super.name = "홍길동";
super.getName();
}
@Override
void attack() {
super.weapon = "총";
super.attack();
}
}
Human 클래스는 사람이기 때문에 말을 할 수 있다. 그래서 Talkable이라는 인터페이스를 상속받아서 구현했다.
Talkable.java
public interface Talkable {
void talk();
}
인터페이스에서 선언한 talk() 메서드는 Human 클래스에서 구현하도록 하였다.
Bird.java
public class Bird extends Animal implements Flyable{
@Override
public void fly() {
System.out.println(" I can fly ~ ");
}
@Override
void getName() {
super.name = "참새";
super.getName();
}
@Override
void attack() {
super.weapon = " 발톱";
super.attack();
}
}
Bird 클래스는 말을 할 수 없기 때문에 Talkable이 아닌 Flyable을 상속받아서 구현했다.
Flyable.java
public interface Flyable {
void fly();
}
Flyable 클래스에서 선언한 fly() 메서드를 Bird 클래스에서 구현했다.
결과
코드를 보면 알 수 있듯이 Animal 클래스에 선언된 메서드는 어느 클래스에서도 사용될 수 있는 메서드이다.
하지만 각 고유한 특징을 나타내는(예를 들어, fly() 메서드나 talk() 메서드) 것들은 인터페이스로 선언해서 구현해주었다.
만약에 Human 클래스에 다른 특징을 구현할 필요가 생긴다면 인터페이스를 생성하고 상속받으면 된다.
예를 들어, 사람은 운전을 할 수 있다. 그래서 Drivable이라는 인터페이스를 만들어주고 상속하여 구현하면 된다.
public class Human extends Animal implements Talkable, Drivable {
@Override
public void talk() {
System.out.println("사람은 말을 할 수 있다.");
}
@Override
void getName() {
super.name = "홍길동";
super.getName();
}
@Override
void attack() {
super.weapon = "총";
super.attack();
}
@Override
public void drive() {
System.out.println("사람은 운전을 할 수 있다.");
}
}
그러면 보다시피 인터페이스를 다중 상속했음에도 에러 없이 결과를 출력해준다. 이것으로 추상 클래스와 인터페이스를 나눠서 사용하는 이유를 어느 정도 알 수 있다고 생각된다.
자바 8 이후 인터페이스에 일반 메서드를 선언할 수 있게 되었다. 단, default, static이라는 키워드를 사용해야 한다.
interfaceEx.java
public interface interfaceEx {
void method1();
default void method2(){
System.out.println("interface 내에 default 메서드");
}
static void method3(){
System.out.println("interface 내에 static 메서드");
}
}
interfaceImpl.java
public class interfaceImpl implements interfaceEx{
public static void main(String[] args) {
interfaceImpl iI = new interfaceImpl();
iI.method1();
iI.method2();
interfaceEx.method3(); // interface에서 static으로 선언했으므로 구현할 필요없이 바로 메서드 사용 가능.
}
@Override
public void method1() { // interface 내에 추상 메서드
System.out.println("interface 내에 추상 메서드");
}
@Override
public void method2() { // default 메서드
interfaceEx.super.method2();
}
}
default를 사용해서 만든 메서드는 상속받는 곳에서 오버라이딩해서 사용할 수 있다.
static을 사용해서 만든 메서드는 상속해서 오버라이딩할 필요 없이 메서드를 바로 사용할 수 있다.
▽ 도움을 주신 분들
'Language > Java' 카테고리의 다른 글
[Java] static 키워드에 대해 알아보자 ! (0) | 2022.06.17 |
---|---|
[Java] 오버로딩 VS 오버라이딩 (0) | 2022.06.14 |
[Java]스트림(stream)(3/3) (0) | 2022.06.09 |
[Java]스트림(stream)(2/3) (0) | 2022.06.07 |
[Java]스트림(stream)(1/3) (0) | 2022.06.07 |