[Java] 객체지향 프로그래밍 4

iiueon·2023년 7월 7일

다형성(polymorphism)

조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것

자손타입 -> 조상타입(Up-casting) : 형변환 생략가능
자손타입 <- 조상타입(Down-casting) : 형변환 생략불가

참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.
참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것 뿐이다.

멤버변수가 조상 클래스와 자손 클래스에 중복으로 정의된 경우,
조상타입의 참조변수를 사용했을 때는 조상 클래스에 선언된 멤버변수가 사용되고, 자손타입의 참조변수를 사용했을 때는 자손 클래스에 선언된 멤버변수가 사용된다.

메서드의 경우 조상 클래스의 메서드를 자손의 클래스에서 오버라이딩한 경우에도 참조변수의 타입에 관계없이 항상 실제 인스턴스의 메서드(오버라이딩한 메서드)가 호출된다.

instanceof

참조변수가 참조하고 있는 인스턴스의 실제타입을 알아보기 위해 사용
어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

Vector클래스

동적으로 크기가 관리되는 객체배열
내부적으로 Object타입의 배열을 가짐

메서드/생성자설명
Vector()10개의 객체를 저장할 수 있는 Vector인스턴스를 생성한다.
10개 이상의 인스턴스가 저장되면, 자동적으로 크기가 증가된다.
boolean add(Object o)Vector에 객체를 추가한다. 추가에 성공하면 결과값으로 true,
실패하면 fasle를 반환한다.
boolean remove(Object o)Vector에 저장되어 있는 객체를 제거한다. 제거에 성공하면 true,
실패하면 false를 반환한다.
boolean isEmpty()Vector가 비어있는지 검사한다. 비어있으면 true, 비어있지 않으면
false를 반환하다.
Object get(int index)지정된 위치(index)의 객체를 반환한다. 반환타입이 Object타입이
므로 적절한 타입으로의 형변환이 필요하다.
int size()Vector에 저장되어 있는 객체의 개수를 반환한다.


추상클래스(abstract class)

추상화 <-> 구체화

추상클래스

추상메서드를 포함하고 있는 클래스.
추상클래스로 인스턴스는 생성할 수 없고, 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.

추상메서드(abstract method)

선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것.
조상으로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손클래스도 추상클래스로 지정해 주어야 한다.

조상 클래스에서 추상메서드로 선언하는 이유는 자손 클래스에서 추상메서드를 반드시 구현하도록 강요하기 위해서이다.



인터페이스(interface)

추상메서드와 상수만을 멤버로 가질 수 있는 일종의 추상클래스

  • 접근제어자로 public 또는 default를 사용할 수 있다.
  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
    단, static메서드와 디폴트 메서드는 예외 (JDK1.8부터)

인터페이스의 상속과 구현

  • 인터페이스는 인터페이스로부터만 상속받을 수 있다.
  • 다중상속이 가능하다.
  • 인터페이스도 추상클래스처럼 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야한다.
  • 구현하는 인터페이스의 메서드 중 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야한다.
  • 상속과 구현을 동시에 할 수도 있다.

리턴타입이 인터페이스라는 것은 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.


인터페이스의 장점

  1. 개발시간을 단축시킬 수 있다.

    • 일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다. 메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다.
    • 그리고 동시에 다른 한 쪽에서는 인터페이스를 구현하는 클래스를 작성하게 하면, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.
  2. 표준화가 가능하다.

    • 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.
  3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.

    • 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.
  4. 독립적인 프로그래밍이 가능하다.

    • 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다.
    • 클래스와 클래스간의 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.

건물클래스
위와 같은 클래스들이 있고, Barrack클래스와 Factory클래스에 건물을 이동시킬 수 있는 새로운 메서드를 추가하고자 한다면 어떻게 해야 할까?

인터페이스구현
위와 같이 해결 가능하다.

새로 추가하고자하는 메서드를 정의하는 인터페이스를 정의하고 이를 구현하는 클래스를 작성한다.

interface Liftable {
	/** 건물을 들어 올린다. */
    void liftOff();
    /** 건물을 들어 올린다. */
    void liftOff();
    /** 건물을 이동한다. */
    void move(int x, int y);
    /** 건물을 정지시킨다. */
    void stop();
    /** 건물을 착륙시킨다. */
    void land();
}

class LiftableImpl implements Liftable {
	public void liftOff()          { /* 내용 생략 */ }
    public void move(int x, int y) { /* 내용 생략 */ }
    public void stop()             { /* 내용 생략 */ }
    public void land()             { /* 내용 생략 */ }
 }

그다음 새로 작성된 인터페이스와 이를 구현한 클래스를 BarrackFactory클래스에 적용하면 된다.
클래스가 Liftable인터페이스를 구현하도록 하고, 인터페이스를 구현한 LiftableImpl클래스를 클래스에 포함시켜서 내부적으로 호출해서 사용하도록 한다.

class Barrack extends Building implements Liftable {
    LiftableImpl l = new LiftableImpl();
    void liftOff() { l.liftOff(); }
    void move(int x, int y) { l.move(x, y); }
    void stop() { l.stop(); }
    void land() { l.land(); }
    void trainMarine() { /* 내용 생략 */ }
    	...
}

class Factory extends Building implements Liftable {
    LiftableImpl l = new LiftableImpl();
    void liftOftt() { l.liftOff(); }
    void move(int x, int y) { l.move(x, y); }
    void stop() { l.stop(); }
    void land() { l.land(); }
    void makeTank() { /* 내용 생략 */ }
     	...
}

인터페이스의 이해

간접관계
직접적인 관계에 있는 두클래스를 위와 같이 인터페이스를 사용해 A-I-B 간접적인 관계로 변경할 수 있다.
클래스 A인터페이스 I하고만 직접적인 관계에 있기 때문에 클래스 B의 변경에 영향을 받지 않는다.

디폴트 메서드와 static메서드

원래 인터페이스에 추상 메서드만 선언할 수 있는데, JDK1.8부터 디폴트 메서드와 static메서드도 추가할 수 있게 되었다.

인터페이스의 모든 메서드는 추상 메서드이어야 한다는 규칙을 따른 대표적인 것으로 java.util.Collection인터페이스가 있다. 이 인터페이스와 관련된 static메서드들은 Collections라는 클래스에 있다.

디폴트 메서드(default method)

디폴트 메서드는 추상 메서드의 기본적인 구현을 제공하는 메서드로, 추상 메서드가 아니기 때문에 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
접근 제어자가 public이며, 생략가능하다.

새로 추가된 디폴트 메서드와 기존 메서드와 이름이 중복되어 충돌하는 경우

  1. 여러 인터페이스의 디폴트 메서드 간의 충돌
    • 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
  2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
    - 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.



참고 자료

  • 자바의 정석 3판
profile
지연_로딩

0개의 댓글