JAVA 06 : final, 추상클래스, 인터페이스

LeeWonjin·2022년 7월 18일

2022 백엔드스터디

목록 보기
6/20

Do it! 자바 프로그래밍 입문 9장~10장

final 키워드

  • final 변수 : 상수 (최초 한 번만 값 할당가능)
  • final 메소드 : 오버라이드할 수 없는 메소드로 지정
  • final 클래스 : 상속할 수 없는 메소드로 지정(다른 클래스가 final클래스를 상속할 수 없음)

클래스의 종류

  • 구상(Concrete) 클래스 : 구현(body)이 있는 클래스
  • 추상(Abstract) 클래스 : 구현이 없는 추상메소드를 포함하는 클래스

추상클래스

개요

상속되기 위해 만드는 클래스.
추상클래스는 인스턴스화될 수 없다. (자식을 참조할 수는 있다.)

접근지시자 abstract class 클래스식별자 {
  abstract 반환형 method_1(매개변수);
  
  반환형 method_2(매개변수){
    구현내용
  }
}
  • method_1

    위 코드에서 method_1은 추상메소드로, body가 없다.
    이 클래스를 상속받는 자식클래스에서 오버라이드하여 구현한다.

    즉, 이 클래스의 자식은 method_1를 구현할 책임(의무)이 있고, 이 클래스는 자식에게 구현을 위임했다. 고 표현할 수 있다.

  • method_2
    method_2는 body가 있는 구현된 메소드이다.

메소드 종류

  • 구현된 메소드 : 일반적인 메소드
  • 추상 메소드 : body가 없는 메소드
  • 훅 메소드 : body가 있지만 비어있는 메소드로, 필요에 따라 자식이 구현
  • 템플릿 메소드 : 추상메소드 또는 구현된 메소드를 활용해 전체 시나리오를 정의하는 메소드.
    • final 키워드를 붙여 자식클래스에서 시나리오를 변경할 수 없도록 제한할 수 있음
abstract class Animal {
	protected int id;
	protected int huntCnt;
	
	public Animal(int id) {
		this.id = id;
		huntCnt = 0;
		System.out.println("No. "+id+" born");
	}
	
	public abstract String move(); // abstract method
	public abstract String attack(); // abstract method
	
	public void bark(){} // hook method
	
	final public void log(String action) {
		System.out.println("[ID #"+id+"] "+action);
	}
	
	final public void hunt() { // Template method
		log(move());
		bark();
		log(attack());
		log(move());
		
		huntCnt++;
	}
	
	public int getHuntCnt() {
		return huntCnt;
	}
}

class Gorani extends Animal {
	public Gorani(int id) {
		super(id);
	}
	
	@Override
	public String move( ) {
		return "run";
	}
	
	@Override
	public String attack() {
		return "bite";
	}
	
	@Override
	public void bark() {
		log("kwwawawkakw!!");
	}	
}

public class Test {
	public static void main(String[] args) {
		Gorani gorani = new Gorani(53);
		gorani.hunt();
		gorani.hunt();
		System.out.println(gorani.getHuntCnt());
		/*
			No. 53 born
			[ID #53] run
			[ID #53] kwwawawkakw!!
			[ID #53] bite
			[ID #53] run
			[ID #53] run
			[ID #53] kwwawawkakw!!
			[ID #53] bite
			[ID #53] run
			2
		*/
	}
}

인터페이스

쓸모

  • 어떤 컴포넌트를 사용할 때 내부 구현에 관계없이 인터페이스만을 보고 인스턴스를 사용
  • 다형성을 구현하는 한 방법

인터페이스가 가질 수 있는 것

  • 상수
    • 필드를 선언하면 pre-compile단계에서 public static final삽입
  • 추상메소드
    • body없는 메소드를 선언하면 pre-compile단계에서 public abstract삽입
  • 디폴트 메소드 : 구현이 있는 메소드, 구현 클래스에서 오버라이드
    • default 키워드
  • 정적 메소드
    • static 키워드
  • private 메소드 : 인터페이스 내부에서 사용하는 메소드 (구현 클래스에서 호출불가)
    • private 키워드
    • private static 키워드

선언

interface POS {
  int MAX = 53; // 상수
  public static getDoubleOfMAX() { // 정적메소드
    return MAX * 2;
  }
 
  private void supportSomthing(){ // private 메소드
    // 구현 내용
  }
  
  int addItem(int itemId); // 추상메소드
  int removeItem(int itemId); // 추상메소드
  default int pay(){
    // 기본 구현 내용
  };
}

구현

abstract class GeneralPOS implements POS {
  @Override 
  public int addItem(int itemId){ ... }
}

class MyPOS implements POS {
  @Override
  public int addItem(int itemId){ ... }
  
  @Override
  public int removeItem(int itemId){ ... }
  
  @Override
  public int pay(){ ... } // 기본구현 메소드도 오버라이드 가능
}

인터페이스를 구현(implement)하면 둘 중 하나가 된다.

  • 모든 것을 구현한 구상메소드
  • 일부를 구현한 추상메소드

인터페이스를 구현한 추상메소드를 상속해 구상메소드를 만들 수도 있다.

형변환

어떤 인터페이스 타입으로 그 인터페이스를 구현한 클래스를 참조할 수 있다.

interface IA {
	int CONST_A = 53;
	void methodA ();
}

class Gorani implements IA {
	@Override
	public void methodA () {
		System.out.println(CONST_A);
	}
}

public class Test {
	public static void main(String[] args) {
		IA gorani = new Gorani();
		gorani.methodA(); // 53
	}
}

여러 개 인터페이스 동시구현

클래스 상속은 하나만 받을 수 있지만, 인터페이스는 여러 개를 동시에 구현할 수 있다. 콤마(,)로 인터페이스 식별자를 구분한다.

public interface IA { }
public interface IB { }
class Gorani implements IA, IB {
  // 구현내용
}
interface IA {
	int CONST_A = 53;
	void methodA ();
}

interface IB {
	int CONST_B = 535353;
	int methodB (int n);
}

class Gorani implements IA, IB {
	@Override
	public void methodA () {
		System.out.println(CONST_A);
	}
	
	@Override
	public int methodB (int n) {
		return n;
	}
	
	void printConstants() {
		System.out.println(CONST_A + " " + CONST_B);
	}
}

public class Test {
	public static void main(String[] args) {
		Gorani gorani = new Gorani();
		gorani.methodA(); // 53
		
		int tmp = gorani.methodB(111);
		System.out.println(tmp); // 111
		
		gorani.printConstants(); // 53 535353
	}
}

인터페이스간 상속

  • 인터페이스도 인터페이스를 상속 가능
  • 여러 개 상속 가능
interface IA {
	void methodA();
}
interface IB extends IA {
	default void methodA() { // 기본구현 메소드
		System.out.println("Default A in IB");
	}
	
	void methodB();
}
interface IC extends IA, IB {
	void methodC();
}

class Class_IB_1 implements IB { 
	// IB인터페이스는 methodA(), methodB() 두 개의 메소드를 포함
	// --->	Class_B는 methodB()에 대해서만 구현의무 있음 
	// 		(methodA는 이미 IB에서 기본구현됨)
	
	@Override
	public void methodB() {
		System.out.println('B');
	}
}

class Class_IB_2 implements IB { 
	// IB인터페이스는 methodA(), methodB() 두 개의 메소드를 포함
	// --->	Class_B는 methodB()에 대해서만 구현의무 있음 
	// 		(methodA는 이미 IB에서 기본구현됨)
	
	@Override
	public void methodA() { // IB의 기본구현 메소드 오버라이드
		System.out.println('A');
	}
	
	@Override
	public void methodB() {
		System.out.println('B');
	}
}

class Class_IC implements IC {
	// IC인터페이스는 methodA(), methodB(), methodC() 세 개 메소드 포함
	// -->	Class_IC는 methodB(), methodC()에 대해 구현의무 있음
	// 		(methodA는 이미 IB에서 기본구현됨)
	
	@Override
	public void methodB() {
		System.out.println("B in Class_IC");
	}
	
	@Override
	public void methodC() {
		System.out.println("C in Class_IC");
	}
}

public class Test {
	public static void main(String[] args) {
		IB ib1 = new Class_IB_1();
		ib1.methodA(); // Default A in IB
		ib1.methodB(); // B

		IB ib2 = new Class_IB_2();
		ib2.methodA(); // A
		ib2.methodB(); // B
		
		IC ic = new Class_IC();
		ic.methodA(); // Default A in IB
		ic.methodB(); // B in Class_IC
		ic.methodC(); // C in Class_IC
	}
}

상속과 동시에 구현

interface I { ... }
class C { ... }

class MyClass extends C implements I {
  // 구현
}
profile
노는게 제일 좋습니다.

0개의 댓글