공부한 기록/Programming Language

Java - 인터페이스와 특수 클래스(추상 클래스)

YongE 2024. 4. 15. 21:03

Java


 

Java와 Java에 대한 특징(컴파일러, JVM의 인터프리팅) 같은 자세한 정보는 시중의 책이나 타 블로그 글에 자세히 나와있으니 언급하지 않고, 여기서는 고급 Java에 대해 공부한 내용만 다룰 계획이다.

 

 

추상 클래스


 

  • 추상 클래스는 상속계층에서 자식 멤버(필드, 메소드)의 이름을 통일하기 위해 사용한다.
  • 자체적으로 객체를 생성할 수 없다.
  • 추상 클래스에는 아예 없거나 하나 이상 있는 추상 메소드가 포함된다.
  • 이 추상 메소드는 ‘무엇을 할지’ 선언하지, ‘어떻게 할지’ 선언하지는 않는다.

추상 클래스의 기본적인 예제를 보자.

abstract class 클래스 이름{
 //필드 ...int a, b;
 //생성자 ... a(){}
 //메소드 ...일반덕으로 하나 이상의 추상 메소드를 포함함.
 //아래는 추상 메소드의 예
 abstract 반환 타입 메서드명();
}

 

이를 직접 구현해보면 다음과 같다.

 

//추상 클래스 shape
abstract class Shape {
	double pi = 3.14;
	abstract void draw();
	public double findArea() {
		return 0.0;
	}
}

//추상 클래스를 상속한 circle 클래스
class Circle extends Shape {
	int radius;
	public Circle(int radius) {
		this.radius = radius;
	}
	public void draw() {
		System.out.println("원을 그리다.");
	}
	public double findArea() {
		return pi * radius * radius;
	}
}

 

위와 같이 추상클래스를 상속했으면 추상 메소드를 오버라이딩하여 '어떻게 할지' 재정의할 수 있다.

 

 

 

인터페이스


 

인터페이스는 '통일된 표준'이며, '정해진 틀'이다. 객체 지향 프로그래밍에서 중요한 개념인 다형성을 보장해주는 Java의 기술이다.

 

  • 인터페이스를 준수하면, 통합에 신경쓰지 않아도, 다양한 형태의 클래스를 개발할 수 있다. 이 통합을 신경쓰는 상황은 기술부채가 눈덩이처럼 불어난 상황이라고 생각된다. 객체 지향의 5가지 원칙을 준수하며, 소프트웨어를 자동차처럼 조립해서 만들 수 있도록 하려면 이 '통합'을 신경 써야 하고 이를 가능케 할 틀이 필요하다. 인터페이스가 그것이다.
  • 클래스는 다중 상속을 지원하지 않는다. 그러나 인터페이스는 다중 상속 효과를 누릴 수 있다.
  • Java에서는 다양한 인터페이스가 기본적으로 제공된다.

다음은 인터페이스의 기본적인 토대를 보겠다.

interface 인터페이스이름 {
	// 상수 필드 → 상수만 가능하기 때문에 public static final 키워드 생략 가능
	// abstract 메서드 → 인터페이스의 모든 메서드(아래 3가지 종류를 제외)가
							public abstract이기 때문에 public abstract 키워드 생략 가능
	// default 메서드
	// static 메서드 -> 정적 메소드는 인터페이스로 직접 호출 가능. (예 : Math.max())
	// private 메서드
}

 

여기서 디폴트 메소드 Default Method에 대해서 궁금할 수 있겠다. 설명하자면 다음과 같다.

 

추상 abstract 메소드를 2개 만들었다면 이 인터페이스를 상속한 클래스는 쓰지 않아도 2번째 메소드를 구현해야 한다. 하지만 디폴트 메소드는 구현하지 않아도 클래스를 실행하는데 에러가 없다. 즉 선택적으로 상속이 가능.

 

 

어쨌든 상수 필드에서는 public static final을 제외 가능하다. 메소드는 위와 같이 4개의 종류가 가능하다.

 

interface Coin { //상수 활용
	int PENNY = 1, NICKEL = 5, DIME = 10, QUARTER = 25;
}

//인터페이스에서의 메소드 정의
public interface Controllable {
	//디폴트 메소드
	default void repair() {
		show("장비를 수리한다.");
	}
    //정적 메소드
	static void reset() {
		System.out.println("장비를 초기화한다.");
	}
    //private 메소드
	private void show(String s) {
		System.out.println(s);
	}
    
    //추상 메소드
	void turnOn();
	void turnOff();
}

 

디폴트 메소드를 재정의하려면 오버라이딩해야 한다. 정적 메소드는 "인터페이스명.메소드명"으로 가능하다.

 

인터페이스는 클래스처럼 하나의 타입이다. 따라서 인터페이스 타입의 변수 선언이 가능하다. 물론 구현 클래스는 인터페이스를 구현한 자식 타입이어야 한다. 구현 객체를 참조하기만 하면 강제 타입 변환이 가능하다.

/* 인터페이스 */
interface Movable {
	void move(int x);
}

 /* 구현 클래스 */
class Car implements Movable {
	private int pos = 0;
	public void move(int x) {
		pos += x;
	}
	public void show() {
		System.out.println(pos + "m 이동했습니다.");
	}
}

//사용 예시
public class MovableDemo {
	public static void main(String[] args) {
		Movable m = new Car(); //인터페이스 타입 변수 = 구현 객체
        
        Car c = (Car) m; //변수가 구현 객체를 참조하면 강제 타입 변환 가능
        //이렇게 하면 Movable 타입에는 없고, Car에만 있는 show()를 사용할 수 있다.
	}
}

 

 

 

익명 클래스


중첩 클래스의 특수한 형태이다. 여러 번 쓸 코드를 하나로 써서 코드가 단순해진다. 코드가 단순해지기에 이벤트처리나 멀티 스레드 등에서 자주 사용된다.

 

익명 클래스에 대한 설명은 이 정도이고, 후에 나올 람다식과 연관되어 있어서 간단한 예제 코드만 남긴다.

 

public class Anonymous {
	public static void main(String[] args) {
		Bird b = new Bird() {
        	/*Bird가 클래스라면 오버라이딩한 메소드를 작성하고, 인터페이스라면 구현한 메소드를 작성해야 함.*/
			public void move() {
				System.out.println("독수리가 난다~~~.");
			}
        };
		b.move();
	}
}
728x90
반응형