본문 바로가기

programming/Java [notion 정리본 업로드]

추상클래스와 인터페이스

A. 추상 클래스와 추상 메서드

추상 클래스

  • abstract 수식어가 사용된 클래스
    • 추상 메서드의 유무와 상관없이 abstract 키워드 사용 가능
  • 대부분이 경우, 추상클래스는 1개 이상의 추상 메서드를 갖고 있는 경우가 많음. (반드시 그런 것은 아님.)
  • 클래스에 추상메서드가 있는 경우에는 반드시 추상 클래스로 선언해야 한다.
  • 추상 클래스는 객체를 생성할 수 없음. 생성자를 호출할 수 없음.

추상 메서드

  • 메서드의 signature (수식어, 리턴 타입 , 메서드 이름, 파라미터 선언)만 선언어 있고 바디가 정의되어 있지 않은 메서드를 의미한다.
    • 즉, 기능이 정의되어 있지 않은 메서드
  • 추상 메서드는 반드시 abstract 키워드로 수식해야 함
  • 추상 메서드 선언은 반드시 세미콜론(;)으로 끝나야 함.
  • 추상 메서드를 정의할 때, 클래스에도 abstract 키워드가 꼭 필요함.
    • 추상 메서드는 추상 클래스에서만 작성할 수 있음

목적

추상 클래스와 추상 메서드는 상속받는 하위 클래스에서의 구현을 목적으로 한다.

하위 클래스에서의 구현을 강제

  • 동물이라는 클래스가 있다고 하자.
  • 동물은 다양한 구체적 동물(개, 새) 등등이 있고, 각자 다 다른 움직임을 갖는다.

<aside> 💡 추상 클래스 Animal 타입으로는 객체 생성이 되지 않음!

abstract class Animal {
	abstract public void move();
}
public class InheritanceMain07 {
	public static void main(String[] args) {
		Animal animal = new Animal();
	}
}
  • 하이라이트 부분에서 문제 발생
    • CANNOT INSTANTIATE THE TYPE ANIMAL </aside>

추상 클래스를 상속하는 클래스를 만들어보자

class Dog extends ~~Animal~~{}

상속한 추상 메서드를 보충해야 한다/.

[예제] 추상 클래스

package com.itwill.inheritance08;

public abstract class Shape {
//	field
	protected String type; // (1)

//	생성자
	protected Shape(String type) {
		this.type = type;
	}

	// 추상 메서드
	public abstract double area();
//	도형의 넓이를 리턴하는 메서드
	public abstract double perimeter();
//	도형의 둘레 길이를 리턴하는 메서드

//	(2)
	public final void draw() {
		String info = String.format("%s[넓이=%2f, 둘레=%f]", type, area(), perimeter());
		System.out.println(info);
	} // (3)

}
  1. 직사각형, 원, 사각형
  2. 일반) final method - 하위 클래스에서 재정의 할 수 없는 메서드
  3. 그래픽 함수 등에서 요런 패턴을
package com.itwill.inheritance08;

public class Rectangle extends Shape {
	private double width; // 직사각형 가로 길이
	private double height; // 직사각형 세로 길이

	protected Rectangle(double width, double height) {
		super("RECTANGLE");
		this.width = width;
		this.height = height;
	}
	@Override
	public double area() {
		return width * height;
	}
	@Override
	public double perimeter() {
		return 2 * (width + height);
	}
}
package com.itwill.inheritance08;

public class InheritanceMain08 {
	public static void main(String[] args) {
//		Rectangle 타입 객체 생성
		Rectangle rect1 = new Rectangle(4, 3);
		rect1.draw();
	}
}
  • 구글이나 카카오 등에서 제공하는 라이브러리 들을 사용할 때도 마찬가지로, 제공하는 추상 클래스를 상속받아 사용할 수 있는 것들이 있다.
    • 추상 메서드들만 특정 클래스들에 맞춰 기능을 만들면, 상위 클래스가 가지고 있는 공통 메서드들을 이용할 수 있다.
    ..???

B. 인터페이스

사용 목적

  • 팀/회사 간의 분업이나 협업을 하기 위해서 메서드들의 signature를 약속하기 위한 규약
  • 인터페이스가 가질 수 있는 멤버들:
    1. public static final 필드 : 즉, 상수만 가질 수 있다.
      • 필드의 수식어는 항상 위와 동일하며, public static final 수식어는 보통 생략된다
    2. public abstract 메서드
      • public abstract 수식어는 보통 생략
  • 인터페이스를 작성할 때, interface 키워드로 선언.
  • 인터페이스는 객체를 생성할 수 없고, 인터페이스를 구현하는 클래스를 작성해서 객체를 생성
  • class 클래스_이름 implements 인터페이스 { 구현부 작성 … }
  • 클래스는 단일 상속만 가능하나, 인터페이스 구현은 개수 제한이 없음.
  • 인터페이스는 상위 인터페이스를 상속(확장)할 수 있다.
    • interface 하위 인터페이스 extends 상위인터페이스 {… …}

인터페이스의 다형성

package com.itwill.interface01;

import com.itwill.database.DatabaseModule;
import com.itwill.database.OracleDatabase;

public class InterfaceMain01 {

	public static void main(String[] args) {
		DatabaseModule db = new OracleDatabase();
//		SuperType var = new SubType(); 다형성
		db.select();
		db.insert(null);
	}

}
  • DatabaseModule은 인터페이스, OracleDatabase는 구현 클래스
    • Super 타입(interface)으로 타입 지정하고, 생성자는 하위타입으로 하면, 실제 구현된 메서드를 사용하면서 만약 기능 변경이 생겨 클래스를 대체하려 하더라도, 생성자의 명만 변경하면 된다.
  • 이전에는 인터페이스가 완전 껍데기만 있는 약속의 도구 였는데 지금은 추상 클래스와의 경계가 모호해진 것이 맞음. (선생님도 가장 큰 단점이 된 것 같다고 말씀하심!)
    • 그렇지만 그럼에도 자바에서 불가능한 다중 상속을 할 수 있는 것이 가장 큰 차이점
    • 또한 목적의 주제가 다르다고 이해하면 될 것 같다.
      • ⇒ 인터페이스 : 분업을 위한
      • ⇒ 추상 클래스 :

import static 문장

  • 인터페이스나 클래스에서 static으로 선언된 필드 또는 메서드 이름을 임포트하는 문장
  • 선언 방법
package com.itwill.interface01;

import com.itwill.database.DatabaseModule;
import com.itwill.database.OracleDatabase;
import static com.itwill.database.DatabaseModule.DB_VERSION;

public class InterfaceMain01 {
	public static void main(String[] args) {
... ...

		System.out.println("버전 = " + DB_VERSION);
		// System.out.println("버전 = " + DatabaseModule.DB_VERSION); 와 동일
	}

  • 자바에서는 임포트 문장들의 순서가 그렇게 중요하지는 않지만, 위와 같은 방식으로
    • 클래스 임포트 문장들
    • ⇒ 스태틱 임포트 문장들
    순으로 나열한다.
  • 에자일…ㅋㅎㅋ

인터페이스가 가질 수 있는 멤버들

자바 8 버전 이상의 변경 사항

인터페이스의 가시성 수식어는 별다른 명시가 없다면 public이다.

  1. [public static final] 필드 ⇒ 필드는 유일해!
  2. [public abstract] 메서드 - body가 없는 추상 메서드
  3. ⇒ 구현 클래스에서 반드시 구현해야 함
  4. [public] static 메서드 - Java 8 부터 가능한 멤버
  5. =>객체 생성 없이, 인터페이스 이름을 접두사로 써서 호출할 수 있는 메서드
  6. [public] default 메서드 - Java 8부터 가능한 멤버 => 인터페이스 안에 있는 바디가 구현된 메서드. (인스턴스 메서드)
    • 바디가 있으면 default 수식어를 꼭 붙여주어야 함.
    • 인터페이스를 구현한 하위타입의 객체를 생성한 후 호출할 수 있는 메서드
  7. private static 메서드 - Java 9 부터
    • 바디가 구현된 메서드. static나 default 메서드에서
    • static (1) 또는 default(2) 메서드에서만 호출할 목적으로 만드는 메서드
  8. private 메서드 ⇒ Java 9부터
    • body가 구현된 메서드
    • default 메서드에서만 호출할 목적으로 만든 메서드
package com.itwill.interface02;

/*
 * 인터페이스가 가질 수 있는 멤버들 - 자바 8 버전 이상의 변경 사항
 * 1. [public static final] 필드
 * 2. [public abstract] 메서드 - body가 없는 추상 메서드 => 구현 클래스에서 반드시 구현해야 함
 * 3. [public] static 메서드 - Java 8 부터 가능한 멤버 =>객체 생성 없이, 인터페이스 이름을 접두사로 써서 호출할 수 있는 메서드
 * 4. [public] default 메서드 - Java 8부터 가능한 멤버 => 인터페이스 안에 있는 바디가 구현된 메서드. (인스턴스 메서드)
 * 								바디가 있으면 default 수식어를 꼭 붙여주어야 함.
 * 5. 
 * 
 */
interface Test {
//	1. [public static final] 필드
	int VERSION = 1;

//	2. [public abstract] 메서드 - body가 없는 추상 메서드
	void test();

//	3. [public] static 메서드 - Java 8 부터 가능한 멤버 
	static void staticMethod() {
		System.out.println("공개 static Method");
	}

//	4. [public] default 메서드 - Java 8 부터 가능한 멤버 
	default void defaultMethod() {
		System.out.println("공개 기본 메서드");
	}
}

class TestImpl implements Test {
	public void test() {
		System.out.println("테스트 코드");
	}
}

public class InterfaceMain02 {
	public static void main(String[] args) {
		Test.staticMethod(); // 인터페이스의 public static 메서드 호출
		Test test = new TestImpl();

		test.defaultMethod();
	}
}                          

C. ENUM(열거형 타입)

Enum Weekday

package com.itwill.switch03;

public enum Weekday {
	SUN, MON, TUE, WED, THU, FRI, SAT;
}

SwitchMain03 Weekday

package com.itwill.switch03;

public class SwitchMain03 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Weekday weekday = Weekday.MON;

		switch (weekday) {
		case MON:
			System.out.println("회사 가기 싫어...");
			break;
		case TUE:
		case WED:
			break;
		case THU:
		case FRI:
			break;
		case SAT:
		case SUN:
			break;
		}
	}

}
  • 상수들만 정의한 특별한 클래스
    • 자바 ver.5부터 나왔음… 이전에는 인터페이스나 클래스로 상수를 정의했음
  • ENUM이 나오기 이전에 사용되었던 상수 클래스/인터페이스 (1번 단계)
    • public static final 이 기본적으로 필드의 속성이 되고 객체 생성되 안되는 interface
    • 정수 타입 switch 케이스로 사용
    • 코드의 가독성을 높이기 위한 방법
package com.itwill.enum01;

public class Meal {
	// 상수들을 정의 :
	public static final int BREAKFAST = 0;
	public static final int LUNCH = 1;
	public static final int DINNER = 2;

	private Meal() {}
}
package com.itwill.enum01;

public interface Meal02 {
	int BREAKFAST = 0;
	int LUNCH = 1;
	int DINNER = 2;
}

CLASS, INTERFACE로 만들면 불편한 점…

  • 매번 상수들을 정의해야 함
  • 각각 다른 값으로 할당을 해줘야함..
  • 다음과 같은 상황에서 매우 위험
    package com.itwill.enum01;
    
    public class EnumTest {
    	public static void main(String[] args) {
    
    		int meal = Meal.BREAKFAST;
    		switch (meal) {
    		case Season.SPRING:
    
    			break;
    
    		case Season.SUMMER:
    
    			break;
    
    		case Season.FALL:
    
    			break;
    		case Season.WINTER:
    
    			break;
    		}
    	}
    }
    
    • 위 코드에서 식사를 기준으로 스위치 케이스를 써야 하는데, int 타입의 값만 비교하기 때문에, 다른 ENUM을 사용하더라도 문법상의 오류가 없다.
  • package com.itwill.enum01; public interface Season { // public static final 상수들 int SPRING = 0; int SUMMER = 1; int FALL = 2; int WINTER = 3; }
  • 객체의 주소값을 이용하여 분기 구분 (2번 단계)
    • 객체의 주소값은 겹칠 일이 없기 때문에 위와 같이 각 Enum 역할을 하는 클래스들을 구분할 수 있다.
    • 객체의 주소값은 switch 케이스 사용이 불가능하기 때문에 if ~ else 구문을 사용해야 함.
    package com.itwill.enum01;
    
    public class Meal2 {
    	private Meal2() {
    	}
    
    	public static final Meal2 BREAKFAST = new Meal2();
    	public static final Meal2 LUNCH = new Meal2();
    	public static final Meal2 DINNER = new Meal2();
    
    }
    
    class Season2 {
    	private Season2() {
    	}
    
    	public static final Season2 SPRING = new Season2();
    	public static final Season2 SUMMER = new Season2();
    	public static final Season2 FALL = new Season2();
    	public static final Season2 WINTER = new Season2();
    
    }
    
    • 위 코드를 그냥 특별한! 타입인 enum 타입을 만들어서 사용한 것!
    • 즉, enum은 각 상수 별로 객체의 주소값을 저장하는 것.
  • enum 안에서도 public 메서드 만들 수 있어여
    • 상수 선언이 끝나버리고 난 후, public 메서드 만들 수 있음 ㄷ
    • static도 되누…
    public enum Meal3 {
    	BREAKFAST, LUNCH, DINNER;
    }
    

 

  • Enum도 생성자를 만들 수 있다.
    • 하지만 private로만 만들 수 있다.
    • 필드를 선언해서 SPRING("봄") 와 같이 상수를 선언할 때 생성자를 호출하여 아규먼트를 넘겨줌
    package com.itwill.enum01;
    
    public enum Season3 {
    	SPRING("봄"), SUMMER("여름");
    
    	private String seasonName;
    
    	private Season3(String season) {
    		this.seasonName = season;
    	}
    
    	public String getSeasonName() {
    		return this.seasonName;
    	}
    
    }
    
    class Season4 { 
    	private String name;
    
    	private Season4(String name) {
    		this.name = name;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public static final Season4 SPRING = new Season3("봄");
    	public static final Season4 SUMMER = new Season3("여름");
    }
    
    • 위 Season3과 Season4는 같은 역할을 한다.
    • Enum인 Season3도 필드를 가질 수 있다… 오호라…

'programming > Java [notion 정리본 업로드]' 카테고리의 다른 글

Collection  (0) 2024.04.13
예외처리 (Exception)  (0) 2024.04.13
상속  (0) 2024.04.13
Class  (0) 2024.04.13
연산자  (0) 2024.04.13