본문 바로가기
자바의정석

자바의정석 - 객체지향개념2

by 승승구리 2021. 12. 19.

상속(ingeritance)

상속이란

기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것

상속의 장점

보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있다.

코드의 추가 및 변경이 매우 용이하다

상속 방법

새로 작성하고자 하는 클래스의 이름 뒤에 상속받고자 하는 클래스의 이름을 키워드'extends'와 함께 써주면 된다.

class Child extends Parent {

}

이 두 클래스는 서로 상속 관계에 있다고 하며, 상속해주는 클래스를 '조상 클래스'라 하고, 상속 받는 클래스를 '자손 클래스'라고 한다.

조상 클래스 : 부모 클래스, 상위 클래스, 기반 클래스
자손 클래스 : 자식 클래스, 하위 클래스, 파생된 클래스

자손 클래스는 조상 클래스의 모든 멤버를 상속 받으므로 항상 조상 클래스보다 같거나 많은 멤버를 갖는다.

즉, 상속을 거듭할수록 상속받는 클래스의 멤버 개수는 점점 늘어나게 된다.

* 나는 많은 상속을 하는 것이 유지보수에 좋다고는 생각하지 않는다... 분석하기 어려울것으로 예상

- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.
class Parent {}
class Child extends Parent {}
class Child2 extends Prent {}

클래스 Child, Child2가 모두 Parent클래스를 상속받고 있으므로, Parent클래스와 Child클래스, 그리고 Parent클래스와 Child2클래스는 서로 상속관계에 있지만 클래스 Child와 Child2클래스는 서로 아무런 관계도 성립되지 않는다.

같은 내용의 코드를 하나 이상의 클래스에 중복으로 추가해야하는 경우에는 상속관계를 이용해서 코드의 중복을 최소화해야한다.
class Tv2 {
    boolean power;
    int channel;

    void power() {
        power = !power;
    }
    void channelUp() {
        ++channel;
    }

    void channelDown() {
        --channel;
    }

}

class CaptionTv extends Tv2 {
    boolean caption;
    void displayCaption(String text) {
        if (caption) {
            System.out.println(text);
        }
    }
}
public class CaptionTvTest {
    public static void main(String[] args) {
    CaptionTv ctv = new CaptionTv();
    ctv.channel = 10;
    ctv.channelUp();
        System.out.println(ctv.channel);
        ctv.displayCaption("Hello, World");
        ctv.caption = true;
        ctv.displayCaption("hello, World");
    }
}

자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버도 함께 생성되기 떄문에 따로 조상 클래스의 인스턴스를 생성하지 않고도 조상 클래스의 멤버들을 사용할 수 있다.

 

클래스간의 관계 - 포함관계

상속이외에도 클래스를 재사용하는 또 다른 방법

-> 클래스간의 포함관계를 맺어 주는 것이다.

-> 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것을 뜻한다.

class Circle {
	int x;
    int y;
    int r;
 }
class Point {
	int x;
    int y;
}
class Circle {                     class Circle {
    int x;                              Point p = new Point();
    int y;           --------->         int r;
    int r;
                                    }
}
하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다. 또한 작성된 단위 클래스들은 다른 클래스를 작성하는데 재사용될 수 있을 것이다.

 

클래스간의 관계 결정하기

그렇다면 언제 상속관계 / 포함관계를 사용할까?

여기서 (-은 -이다. is-a) (-은 -을 가지고 있다. has-a) 개념을 사용한다.

원(Circle)은 점(Point)이다. - Circle is a Point.
원(Circle)은 점(Point)를 가지고 있다. - Circle has a Point.

이처럼 클래스를 가지고 문자을 만들었을때.

is-a가 성립한다면 -> "상속관계"
has-a가 성립한다면 -> "포함관계"

그래서 Circle 클래스와  Point클래스의 관계는 상속관계보다 포함관계를 맺어주는 것이 더 옳다.

상속관계 '~은 ~이다. (is-a)'
포함관계 '~은 ~을 가지고 있다. (has-a)'

* 프로그램에 사용되는 모든 클래스를 분석하여 가능한 많은 관계를 맺어 주도록 노력해서 코드의 재사용성을 높여야 한다.


단일상속(single inheritance)

자바에서는 단일 상속만을 허용한다. 그래서 하나이상의 클래스로부터 상속을 받을 수 없다.

다중상속을 허용하면 여러 클래스로부터 상속받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다는 장점이 있지만, 클래스간의 관계가 매우 복잡해진다는 것과 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점을 가지고 있다.

 

Obejct클래스 - 모든 클래스의 조상

Obejct클래스는 모든 클래스의 최상위에 있는 조상 클래스이다.

- 모든 클래스들은 자동적으로 Obejct클래스를 상속 받는다.

그동안 toString() 이나 equals(Obejct o)와 같은 메서드를 따로 정의하지 않고도 사용할 수 있었던 이유는 이 메서드들이 Object클래스에 정의된 것들이기 때문이다.

 

오버라이딩(overriding)

오버라이딩이란?

- 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 한다.

- 상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스 자신에게 맞게 변경해야 하는 경우가 많다.

 

오버라이딩의 조건

- 오버라이딩은 메서드의 내용!! 만을 새로 작성하는 것이므로 메서드의 선언부는 조상의 것과 완전히 일치해야 한다.

1. 메서드 이름이 같아야 한다.
2. 매개변수가 같아야 한다.
3. 반환타입이 같아야 한다.

오버라이딩시 주의할점

1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.
3. 인스턴스 메서드를 static메서드로 또는 그 반대로 변경할 수 없다.

조상 클래스에 정의된 static메서드를 자손 클래스에서 똑같은 이름의 static메서드로 정의할 수 는 있으나, 오버라이딩은 아니다. static 멤버는 해당 클래스에만 묶여있다고 생각해야 한다.

 

오버로딩 vs 오버라이딩

오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는 것 (new)
오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것(change, moidfy)

super

spuer란?

- 자손클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수

- 멤버변수와 지역변수의 이름이 같을 때 this를 사용해서 구별하는 것 처럼 상속받은 멤버와 자신의 클래스에 정의된 멤버의 이름이 같을 때에는 super를 사용해서 구별한다.

super() - 조상 클래스의 생성자

super()란?

- super()는 조상 클래스의 생성자를 호출하는데 사용한다.

- 자손 클래스의 생성자의 첫줄에서 조상클래스의 생성자를 호출 해야하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다.

Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자, this() 또는 super(),를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super()'를 생성자의 첫 줄에 삽입한다.

인스턴스를 생성할 때는 클래스를 선택하는 것만큼 생성자를 선택하는 것도 중요한 일이다.

1. 클래스 : 어떤 클래스의 인스턴스를 생성할 것인가?
2. 생성자 : 선택한 클래스의 어떤 생성자를 이용해서 인스턴스를 생성할 것인가?
public class PointTest {
    public static void main(String[] args) {
        Point3D p3 = new Point3D(1, 2, 3);
    }
}

class Point2 {
    int x;
    int y;

    Point2(int x, int y) {
        this.x = x;
        this.y = y;
    }

    String getLocation() {
        return "x : " + x + ", y : " + y;
    }
}

class Point3D extends Point2 {
    int z;
    Point3D(int x, int y, int z) {
        // 여기에 컴파일러가 super();를 삽입한다.
        super(x, y);
        this.x = x;
        this.y = y;
        this.z = z;
    }

    String getLocation() {
        // 오버라이딩
        return getLocation() + ",z : " + z;

    }
}

package와 import

패키지(package)

패키지란?

- 클래스의 묶음

- 클래스 또는 인터페이스를 포함

- 관련된 클래스들끼리 그룹 단위로 묶어 놓음 (효율적으로 관리가능)

- 같은 이름의 클래스 일지라도 다른 패키지에 존재한느 것이 가능

- 하나의 소스파일에는 첫 번째 문장으로 단 한번의 패키지 선언만을 허용한다.
- 모든 클래스는 반드시 하나의 패키지에 속해야 한다.
- 패키지는 점(.)을 구분자로 하여 계층구조로 구성할 수 있다.
- 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리이다.

 

패키지의 선언

- 클래스나 인터페이스의 소스파일(.java)에 한 줄만 적어주면 된다.

package 패키지명;

하나의 소스파일에 단 한번만 선언될 수 있다.

패키지명은 대소문자를 모두 허용하지만, 클래스명과 쉽게 구분하기 위해서 소문자를 원칙으로 한다.

 

import문

- 다른 패키지에 있는 클래스를 사용하려면 패키지명이 포함된 클래스 일므을 사용해야 한다. 하지만, 매번 패키지명을 붙여서 작성하려면 불편할 것이다.

- 다른 클래스의 패키지를 미리 import문으로 명시해주면 소스코드에 사용되는 클래스이름에서 패키지명은 생략할 수 있다.

import문의 역할

- 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하는 것이다.

- 컴파일 시에 컴파일러는 import문을 통해 소스파일에 사용된 클래스들의 패키지를 알아 낸 다음, 모든 클래스이름 앞에 패키지명을 뭍여 준다.

 

 

import문의 선언

- 모든 소스파일(.java)에서 import문은 package문 다음에, 그리고 클래스 선언 문 이전에 위치해야 한다.

- import문은 여러 번 선언할 수 있다.

소스파일(.java)의 궂성

1. package 문
2. import 문
3. 클래스 선언
import java.text.SimpleDateFormat;
import java.util.Date;

public class ImportTest {
    public static void main(String[] args) {
        Date today = new Date();

        SimpleDateFormat date = new SimpleDateFormat("yyyy/MM/dd");
        SimpleDateFormat time = new SimpleDateFormat("hh:mm:ss a");

        System.out.println("오늘 날짜는 " + date.format(today));
        System.out.println("현재 시간은 " + time.format(today));
    }
}

static import문

static import문은 static멤버를 호출할 때 클래스 이름을 생략할 수 있다.

import static java.lang.System.out;
import static java.lang.Math.*;

public class StaticImportEx1 {
    public static void main(String[] args) {
        // System.out.println(Math.random());
        out.println(random());
    }
}

 

제어자(modifier)

제어자란?

- 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여한다.

접근 제어자 - public, protected, default, private
그         외 - static, final, abstract, naive, transient, synchronized, volatile, strictfp

접근 제어자를 제외한 제어자는 여러 제어자를 혼합해서 사용하는 것이 가능하다.

 

static - 클래스의, 공통적인

static?

- static은 '클래스의" 또는 '공통적인' 의미를 가지고 있다.

- 인스턴스변수는 하나의 클래스로부터 생성되었더라도 각기 다른 값을 유지하지만, 클래스변수(static멤버변수)는 인스턴스에 관게없이 같은 값을 갖는다. 그 이유는 하나의 변수를 모든 인스턴스가 공유하기 때문이다.

인스턴스 메서드와 static 메서드의 차이

- 메서드 내에서 인스턴스 멤버를 사용하는가의 여부

static이 사용될 수 있는 곳 - 멤버변수, 메서드, 초기화 블럭
제어자 대상 의미
static 멤버변수 - 모든 인스턴스에 공통적으로 사용되는 클래스 변수가 된다.
- 클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.
- 클래스가 메모리에 로드될 때 생성된다.
메서드 - 인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 된다.
- static 메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다.

* 인스턴스 멤버를 사용하지 않는 메서드는 static을 붙여서 static메서드로 선언하는 것을 고려해보도록 하자.

* static 초기화 블럭은 클래스가 메모리에 로드될 때 단 한번만 수행되며, 주로 클래스변수(static변수)를 초기화하는데 주로 사용된다.

 

final - 마지막의, 변경될 수 없는

final?

- final은 '마지막의' 또는 '변경될 수 없는'의 의미를 가지고 있으며 거의 모든 대상에 사용될 수 있다.

변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩을 할 수 없게 되고, 클래스에 사용되면 자신을 확장한느 자손클래스를 정의하지 못하게 한다 (상속불가)

final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수
제어자 대상 의미
final 클래스 변경될 수 없는 클래스, 확장될 수없는 클래스가 된다.
그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
메서드 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.
지역변수
final class FinalTest { // 상속불가
    final int MAX_SIZE = 10; // 상수
    
    final void getMaxSize() { // 오버라이딩 불가
        final int LV = MAX_SIZE; // 상수
        return;MAX_SIZE;
    }
}

abstract - 추상의, 미완성의

abstract?

- abstract는 '미오나성'의 의미를 가지고 있다.

- 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용된다.

abstract가 사용될 수 있는 곳 - 클래스, 메서드

접근 제어자(access modifier)

접근 제어자?

- 멤버 또는 클래스에 사용되어, 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.

- 클래스나 멤버변수, 메서드, 생성자에 접근 제어자가 지정되어 있지 않다면, 접근 제어자가 default임을 뜻한다.

접근 제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드, 생성자
private - 같은 클래스 내에서만 접근이 가능
default - 같은 패키지 내에서만 접근이 가능
protected - 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근이 가능
public - 접근 제한이 전혀 없다
제어자 같은 클래스 같은 패키지 자손 클래스 전체
public O O O O
protected O O O  
(default) O O    
private O      
접근 범위가 넓은 쪽에서 좁은 쪽의 순으로 왼쪽 부터 나열
public > protected > default > private

접근 제어자를 이용한 캡슐화

- 클래스나 멤버, 주로 멤버에 접근 제어자를 사용하는 이유는 클래스의 내부에 선언된 데이터를 보호하기 위해서이다.

- 데이터가 유효한 값을 유지하도록, 또는 비밀번호와 같은 데이터를 외부에서 함부로 변경하지 못하도록 하기 위해서는 외부로부터의 접근을 제한하는 것이 필요하다.

- 이것을 데이터 감추기(data hiding)이라고 하며, 개체지향개념의 캡슐화(encaptulation)에 해당되는 내용이다.

- 클래스 내에서만 사용되는, 내부 작업을 위해 임시로 사용되는 멤버변수나 부분작업을 처리하기 위한 메서드 등의 멤버들을 클래스 내부에 감추기 위해서이다.

접근 제어자를 사용하는 이유
- 외부로부터 데이터를 보호하기 위해서
- 외부에는 불필요한, 내부적으롬나 사용되는, 부분을 감추기 위해서

getter and setter

자바에서는 보통 멤버변수의 값을 읽는 메서드의 이름을 'get멤버변수이름'
멤버변수의 값을 변경하는 메서드의 이름을 'set멤버변수이름'으로 한다.

생성자의 접근 제어자

- 생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다.

- 보통 생성자의 접근 제어자는 클래스의 접근 제어자와 같지만, 다르게 지정할 수도 있다.

- 생성자의 접근 제어자를 private로 지정시, 외부에서 생성자에 접근할 수 없으므로 인스턴스를 생성할 수 없게 된다.

대신 인스턴스를 생성해서 반환해주는 public 메서드를 제공함으로써 외부에서 이 클래스의 인스턴스를 사용하도록 할 수 있다.

이 메서드는 public인 동시에 static이어야 한다.

calss Singleton {
	//...
    private static Singleton s = new Singleton(); // getInstance()에서 사용할 수 있도록 인스턴스가 미리 생성되어야 하므로 static이어야 한다. 
    
    private Singleton() {
    
    }
    
    // 인스턴스를 생성하지 않고도 호출할 수 있어야 하므로 static이어야 한다.
    public static Singleton getInstance() {
    	return s;
    }
}

제어자(modifier)의 조합

대상 사용가능한 제어자
클래스 public, default, final, abstract
메서드 모든 접근 제어자, final, abstract, static
멤버변수 모든 접근 제어자, final, static
지역변수 final

제어자를 조합해서 사용할 때 주의점

1. 메서드에 static과 abstract를 함께 사용할 수 없다.
- static메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문이다.

2. 클래스에 abstract와 final을 동시에 사용할 수 없다.
- 클래스에 사용되는 final은 클래슬르 확장할 수 없다는 의미이고 abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모순되기 때문이다.

3. abstract 메서드의 접근 제어자가 private일 수 없다.
- abstract 메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손 클래스에서 접근할 수 없기 때문이다.

4. 메서드에 private과 final을 같이 사용할 필요는 없다.
- 접근 제어자가 private인 메서드는 오버라이딩을 할 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.

다형성(polymorphism)

다형성이란?

- 객체지향개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미

- 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함으로써 다형성을 프로그램적으로 구현하였다.

- 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다.

Tv t = new Tv();
CaptionTv c = new CaptionTv();

- 위처럼 보통은 인스턴스의 타입과 참조변수의 타입이 일치하는 것이 보통이지만, Tv와 CaptionTv클래스가 서로 상속관계에 있을 경우, 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.

Tv t = new CaptionTv(); // 조상 타입의 참조변수로 자손 인스턴스를 참조

같은 타입의 참조변수로 참조하는 것과 조상타입의 참조변수로 참조하는 것은 어떤 차이가 있을까?

 - 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

CaptionTv c = new Tv();

- 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 존재하지 않는 멤버를 사용하고자 할 가능성이 있으므로 허용하지 않는 것이다. 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스의 멤버 개수보다 같거나 적어야 한다.

- 그렇다면, 인스턴스의 타입과 일치하는 참조변수를 사용하면 인스턴스의 멤버들을 모두 사용할 수있을 텐데 왜 조상타입의 참조변수를 사용해서 인스턴스의 일부 멤버만을 사용하도록 할까?

 

참조변수의 형변환

기본형 변수와 같이 참조변수도 형변환이 가능하다.

단, 서로 상속관계에 있는 클래스사이에서만 가능하다.

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

조상타입의 참조변수를 자손타입의 참조변수로 변환하는 것을 다운캐스팅(down-casting)이라고 하며, 자손타입의 참조변수를 조상타입의 참조변수로 변환하는 것을 업캐스팅(up-casting)이라고 한다.

 

instanceof연산자

- 참조변수가 참조되고 있는 인스턴스의 실제 타입을 알아볼때 사용

- instanceof를 이용한 연산결과로 true를 얻었다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것을 뜻한다.

void doWork(Car c) {
	if ( c instanceof FireEngine) {
    	FireEngine fe = (FireEngine) c;
        fe.water();
    } else if (c instanceof Ambulance) {
    	Amulance a (Ambulance)c;
        a.siren();
    }

}

위의 코드는 Car타입의 참조변수 c를 매개변수로 하는 메서드이다.

조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있기 때문에, 참조변수의 타입과 인스턴스의 타입이 항상 일치하지는 않는다.

 

참조변수와 인스턴스의 연결

조상 타입의 참조변수와 자손타입의 참조변수의 차임점은 사용할 수 있는 멤버의 개수이다.

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

 

추상클래스(abstract class)

추상클래스란?

클래스르 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있다.

- 미완성 메서드를 포함하고 있다는 의미이다.

- 추상클래스 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖는다. 새로운 클래스를 작성할때 아무것도 없는 상태에서 시작하는 것보다는 완전하지는 못하더라도 어느 정도 틀을 갖춘 상태에서 시작하는 것이 나을 것이다.

- 추상클래스는 키워드 'abstract'를 붙이기만 하면 된다.

abstract class 클래스이름 {

}

추상클래스는 추상메서드를 포함하고 있다는 것을 제외하고 일반클래스와 전혀 다르지 않다.

 

추상메서드(abstract method)

메서드는 선언부와 구현부(몸통)으로 구성되어 있다. 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨둔것이 추상메서드이다.

- 메서드를 이와 같이 미완성 상태롤 남겨 놓는 이유는 메서드의 내용이 상속받ㄴ느 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언부만을 작성하고, 구현부는 상속받는 클래스에서 구현하도록 비워 두는 것이다.

/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다. */
abstract 리턴타입 메서드이름();

추상클래스로부터 상속받는 자손클래스는 오버라이딩을 통해 조상인 추상클래스의 추상메서드를 모두 구현해주어야 한다.

 

추상클래스의 작성

여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고, 기존의 클래스의 공통적인 부분을 뽑아서 추상클래스로 만들어 상속하도록 하는 경우도 있다.

추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 : 상속을 통해 클래스를 구현, 확장하는 작업

 

인터페이스 (interface)

인터페이스란?

- 인터페이스는 일종의 추상클래스이다. 인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다.

인터페이스 작성

인터페이스는 class 대신 interface 키워드를 사용한다는 것만 다르다.

interface 인터페이스이름 {
	public static final 타입 상수이름 = 값;
    public abstract 메서드이름(매개변수목록);
}
- 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
단, static메서드와 디폴트 메서드는 예외(JDK 1.8부터 변경)

 

인터페이스의 상속

- 인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속, 즉 여러개의 인터페이스로부터 상속을 받는것이 가능하다.

 

인터페이스의 구현

- 인터페이스도 추상클래스처럼 그 자체로는 인스턴스를 생성할 수 없으며, 인터페이스도 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 하는데, 그 방법은 'implements' 키워드를 사용한다.

 

인터페이스의 장점

- 개발시간을 단축시킬 수 있다.
- 표준화가 가능하다.
- 서로 관게없는 클래스들에게 관게를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다

1. 개발시간을 단축

- 일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다. 메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다.

 

2. 표준화가 가능하다.

- 프로젝트에 사용되는 기본 틀을 인터페이스로 작성하 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.

 

3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.

- 서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않는 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.

 

4. 독립적인 프로그램이 가능하다.

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

 

인터페이스의 이해

- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 된다. (내용은 몰라도 된다.)

디폴트 메서드와 static메서드

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

static 메서드는 인스턴스와 관계가 없는 독립적인 메서드이기 떄문에 예전부터 인터페이스에 추가하지 못할 이유가 없었다.

인터페이스의 static메서드의 접근 제어자는 항상 public이며, 생략할 수 있다.

 

디폴트 메서드

조상 클래스에 새로운 메서드를 추가하는 것은 별 일이 아니지만, 인터페이스의 경우에는 보통 큰 일이 아니다. 인터페이스에 메서들르 추가한다는 것은, 추상 메서드를 추가한다는 것이고, 이 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메서드를 구현해야 하기 때문이다.

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

디폴트 메서드는 앞에 default 키워드를 붙이며, 추상 메서드와 달리 일반 메서드처럼 몸통{}이 있어야 한다.

 

 

내부 클래스(inner class)

내부 클래스는 클래스 내에 선언된다는 점을 제외하고는 일반적인 클래스와 다르지 않다

- 클래스 내에 선언된 클래스 이다.

- 클래스에 다른 클래스를 선언하는 이유는 두 클래스가 서로 긴말한 관계에 있기 때문이다.

내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
- 코드의 복잡성을 줄일 수 있다.(캡슐화)

내부클래스의 종류와 특징

내부 클래스 특징
인스턴스 클래스(instance class) 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스멤버처럼 다루어진다. 주로 외부 클래스의 인스턴스멤버들과 관련된 작업에 사용될 목적으로 선언된다.
스태틱 클래스 (static class) 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static멤버처럼 다루어진다. 주로 외부 클래스의 static멤버, 특히 static메서드에서 사용될 목적으로 선언된다.
지역 클래스 (local class) 외부 클래스의 메서드나 초괴화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있다.
익명 클래스 (anonymous class) 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)

 

익명 클래스(anonymous class)

- 다른 내부 클래스들과는 달리 이름이 없다.

- 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.

new 조상클래스이름() {

}

new 구현인터페이스이름() {

}

이름이 없기 때문에 생성자도 가질 수 없으며, 조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다.

오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만을 구현할 수 있다.