1. 다형성 (polymorphism)

다성형이란?
객체 지향 개념에서 다형성이란 '여러 가지 형태를 가질 수 있는 능력'을 의미한다.
자바에서 한 타입의 참조 변수로 여러 타입의 객체를 참조할 수 있도록 한다.

class Tv {
    int one;
    int two;
}

class CaptionTv extends Tv {
    int three;
}

CaptionTv c = new CaptionTv(); // 1번
Tv c = new CaptionTv(); // 2번

위의 예제에서 1번은 동일한 타입으로 모든 멤버를 사용 가능하고 2번은 CaptionTv에는 Tv가 가지고 있는 모든 멤버를 가지고 있어서 Tv 클래스 타입으로 참조 변수를 사용할 수 있다.
ex) CaptionTv c = new Tv();
하지만 위의 예제처럼 TV 클래스는 CaptionTv 클래스의 멤버를 가지고 있지 않기 때문에 인스턴스가 생성 불가능하다.

 

* 정리

  1. 부모 타입의 참조 변수로 자식 타입의 인스턴스를 참조할 수 있다.
  2. 반대로 자식 타입의 참조변수로 부모 타입의 인스턴스를 참조 할수 없다.

1.1. 참조 변수의 형 변환

참조 변수도 형 변환이 가능하며 상위 타입에서 하위 타입 뿐만 아니라 반대로도 형변환이 가능하며 상속 관계에 있는 모든 타입이 형변환 가능하다.
기본형의 형 변환과 마찬가지로 하위 타입이 상위 타입으로 자동 형변환 된다.

* 생략 여부

  • up-casting : 하위 타입 -> 상위 타입 // 형변환 생략 가능
  • down-casting : 상위 타입 -> 하위 타입 // 형변환 생략 불가

형 변환 시 캐스팅 연산자인 '(클래스)'를 사용한다.
down-casting에서만 캐스팅 연산자를 사용하는 이유는 down-casting 할 때 어떤 타입으로 갈 것인지 명시가 필요하기 때문이다.
형 변환은 인스턴스를 변환하는 것이 아닌 참조 변수를 변환하는 것이다.

Tv tv = (Tv) new CaptionTv();

2. instanceof 연산자

instanceof 연산자란?
참조 변수가 참조하고 있는 인스턴스에 실제 타입을 알아보기 위한 연산자이다.

if(c instanceof Parent) { // true or false
    ...
}

위의 예제는 참조 변수 c가 검사 타입인 Parent 클래스로 형 변환 여부를 체크하는 예제이다.

상위 타입의 참조 변수로 하위 타입의 인스턴스를 생성할 수 있기 때문에 instanceof로 타입 체크를 할 필요가 있다.
어떤 타입에 대한 instanceof연산의 결과가 true라는 것은 검사한 타입으로 형 변환이 가능하다는 뜻이다.

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

메서드의 경우 타입에 상관없이 항상 실제 인스턴스의 메서드(오버라이딩 된 메서드)가 호출된다.
멤버 변수의 경우 상위, 하위 클래스에서 중복으로 정의된 경우 상위 클래스에서는 상위 멤버를 하위 클래스에서 하위 멤버를 사용한다.

4. 매개변수의 다형성

메서드의 매개변수를 상위 클래스로 선언하고 매개변수로 하위 클래스를 받아 사용한다.

class Parent {}
class Child extends Parent {
    method(Parent p) {}
}

Child c = new Child();
c.method(new Child()) {}

5. 여러 종류의 객체를 배열로 다루기

상속 관계또

Product[] p = new Product[3];

p[0] = new Tv();
p[1] = new Computer();
p[2] = new Radio();

Vactor 클래스는 배열의 크기를 동적으로 관리한다.

'개발 언어 > Java' 카테고리의 다른 글

[Java] 인터페이스  (0) 2021.08.27
[Java] 추상 클래스&추상 메서드  (0) 2021.08.26
[Java] 제어자  (0) 2021.08.25
[Java] Package와 import  (0) 2021.08.24
[Java] 상속  (0) 2021.08.24

제어자

제어자란?

클래스와 클래스 멤버 선언 시 사용하여 부가적인 의미를 부여하는 키워드이다. 제어자에는 접근 제어자와 기타 제어자로 나뉜다.

1. 제어자의 종류

클래스, 변수, 메서드에 사용되면 접근 제어자는 한 가지만 사용 가능하다.

1.1. 접근 제어자

종류 : public, protected, default, private

1.2. 기타 제어자

종류 : static, final, abstract, native, tansient, synchronized, volatile, strictfp

1.2.1. static - 클래스의, 공통적인

클래스와 관계된 접근 자이며 사용하는 곳은 멤버 변수, 메서드, 초기화 블록이다.

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

  • 사용하는 곳 : 클래스, 메서드, 멤버 변수, 지역 변수
  • 클래스 : 상속받을 수 없는 클래스
  • 메서드 : 오버 라이딩될 수 없는 클래스
  • 멤버 변수, 지역변수 : 값을 변경할 수 없는 상수

생성자를 이용한 final 멤버 변수의 초기화 선언 시 초기화를 안 하고 생성자에서 단 한 번만 초기화한다. 선언과 동시에 초기화되면 모든 인스턴스는 같은 값을 갖게 되어 생성자에서 매개변수로 받아 초기화해준다.

class Sample {
    final int NUMBER;
    
    Sample(int n) {
        this.NUMBER = n;
    }
}

1.2.3. abstract - 추상의, 미완성의

  • 사용되는 곳 : 클래스, 메서드
  • 클래스 : 클래스 내에 추상 메서드가 선언되어 있음을 의미
  • 메서드 : 선언 부만 작성하고 구현부는 작성하지 않는 추상 메서드임을 알린다.

메서드 선언 부만 작성하고 로직은 구현하지 않는 추상 메서드를 선언하는 데 사용한다.

추상 클래스는 미완성 임으로 인스턴스를 생성할 수 없다.

인스턴스를 생성하지 못하도록 사용하기도 한다.

public abstract class Parent {
    public abstract int get();
    public abstract boolean set();
}

public Child extends Parent {
    public int get() {}
    public boolean set() {}
}

1.3. 접근 제어자 (access modifier)

사용되는 곳 : 클래스, 메서드, 멤버 변수, 생성자

1.3.1. 종류

  • private : 같은 클래스 내에서만 접근 가능
  • default : 같은 패키지 내에서만 접근 가능, 제어자를 지정하지 않으면 접근 제어자가 default임을 뜻
  • protected : 같은 패키지 내에서, 다른 패키지의 하위 클래스에서 접근 가능
  • public  : 접근 제한 없음

1.3.2. 범위 순서

public > protected > default > private

1.3.3. 대상에 따른 접근 제어자

  • 클래스 : public, (default)
  • 메서드, 멤버 변수 : public, protected, default, private
  • 지역 변수 : 없음

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

  1. 외부로부터 데이터를 보호하기 위함
  2. 외부에서 불필요한 내부적으로만 사용되는 부분을 감추기 위함

메서드를 변경했을 때 접근 제어자에 따라 수정, 테스트해야 하는 부분이 달라질 수 있다. 또한 멤버 변수에 잘못된 값을 넣어도 막을 방법이 없어 멤버 변수를 private으로 하고 setter를 이용하여 유효성 검사를 마치고 변수를 변경한다.

public 클래스는 파일에 단 한 개만 존재 가능하고 public 클래스 이름과 파일 이름이 같아야 한다.

public class Sample {
    private int n; // 외부에서 접근 불가
    
    public setN(int n) { // 외부에서 접근 가능하기 때문에 생성자를 통해 내부로 들어와 private n 을 변경
        this.n = n;
    }
}

1.5. 생성자의 접근 제어자 (singleton)

생성자에 private 부여하여 외부에서 인스턴스 생성을 못하도록 막는다. 대신 public으로 인스턴스를 return 해주는 메서드를 제공하여 인스턴스를 받는다.

public class Singleton {
    private static Singleton s = new Singleton();
    
    private singleton() {}
    
    public static Singleton getInstance() {
    	return s;
    }
}


main() {
	Singleton s = Singleton.getInstance();
}

생성자가 private일 경우 상속 클래스로 사용 불가하다. 하위 인스턴스가 생성되면서 상위 인스턴스도 생성되어야 하는데 생성자가 없이 생성이 불가능하다.

1.6. 제어자 (modifier)의 조합

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

1.7. 제어자 조합의 주의

  1. 메서드에 static과 abstract를 같이 사용할 수 없다.
    - static은 구현 부분이 있는 메서드에만 사용 가능하다.
  2. 클래스에 final과 abstract를 동시에 사용할 수 없다.
    - final은 확장이 불가고 abstract는 상속을 통해 완성되기 때문에 모순이다.
  3. abstract 메서드에 접근제어자는 private을 사용할 수 없다.
    - abstract는 하위 클래스에서 완성시켜야 하는데 private은 하위 클래스에서 접근이 불가능하다.
  4. 메서드에 final과 priavet을 같이 사용할 필요 없다.
    - 둘 다 같은 의미라 하나만 사용해도 된다.

'개발 언어 > Java' 카테고리의 다른 글

[Java] 추상 클래스&추상 메서드  (0) 2021.08.26
[Java] 다형성  (0) 2021.08.25
[Java] Package와 import  (0) 2021.08.24
[Java] 상속  (0) 2021.08.24
[Java] 변수의 초기화  (0) 2021.08.24

1. 패키지 (package)

  • 하나의 소스 파일에는 첫 번째 문장으로 단 하나의 패키지 선언만 허용한다.
  • 모든 클래스는 반드시 하나의 패키지에 속해야 한다.
  • 패키지는 점(.)을 구분자로 하여 계층 구조로 구성된다.
  • 패키지는 물리적인 클래스 파일(.class)을 포함하는 하나의 디렉터리이다.
  • 같은 이름의 클래스가 다른 패키지에 있을 수 있다.
// 디렉토리 : com/company/sample
package com.company.sample;

class Sample {}

1.1. 패키지 선언

package com.company.sample; // 패키지 선언
  • 주석과 공백을 제외한 가장 첫 번째 라인에 선언한다.
  • 하나의 소스 파일에 단 한 번만 선언 가능하다.
  • 선언된 패키지안에 해당 클래스가 속해야 한다.
  • 패키지 명은 대소문자 구분이 되지만 클래스와 구분을 하기 위해 소문자로 하는 것을 원칙으로 한다.
  • 자바에서 기본적으로 '이름 없는 패키지(unnamed package)가 있어 패키지를 선언하지 않고도 문제없이 사용할 수 있다.

2. import 문

  • 다른 패키지의 클래스를 사용하려면 패키 지명 + 클래스 이름을 사용해야 한다. 이런 점이 불편하여 import를 통해 미리 선언할 수 있다.
  • 컴파일러는 import문을 보고 각 클래스 명 앞에 패키지 명을 붙인다.
  • 같은 패키지 내의 클래스 들은 import문을 저장하지 않고 패키지 명 생략 가능하다.
package com.company.sample;

import java.util.Scanner; // Sacnner 선언
import java.util.StringTokenizer; // StringTokenizer 선언

class Sample {
    main() {
        // 패키지명 + 클래스 이름
        // 이런 부분이 불편하여 import로 미리 선언
        java.util.Scanner sc = new java.util.Scanner(System.in); 
    }
}

2.1. import 선언문

import java.util.Scanner;
import java.util.*; // 패키지 하위 클래스 전부 미리 선언

'*'는 하위 패키지의 클래스까지 포함하는 것이 아니다.

 

java.lang은 묵시적으로 모든 소스파일에 선언되어 있어 언제든지 사용 가능하다.

2.2. static import 문

static 멤버로 호출할 때 클래스 명을 생략할 수 있다.

import static java.lang.Integer;
import static java.lang.Math.random;
import static java.lang.System.ou;

System.out.println(Math.random());

System.out.println(random()); // 클래스 명 생략

'개발 언어 > Java' 카테고리의 다른 글

[Java] 다형성  (0) 2021.08.25
[Java] 제어자  (0) 2021.08.25
[Java] 상속  (0) 2021.08.24
[Java] 변수의 초기화  (0) 2021.08.24
[Java] 생성자  (0) 2021.08.24

상속

상속이란?

말 그대로 부모에게 물려받는다는 뜻이다. 상위 클래스의 멤버를 하위 클래스가 모두 사용할 수 있으며 기존의 클래스를 재사용하여 새로운 클래스를 만드는 것을 말한다.

1. 상속의 정의와 장점

1.1. 정의

'extends' 키워드를 이용하여 상속을 받는다. 

class Child extends Parent {}

1.2. 장점

  1. 적은 양의 코드로 새로운 클래스를 생성할 수 있다.
  2. 코드를 공통으로 관리하기 때문에 추가 및 변경이 용이하다.
  3. 코드의 중복을 제거하고 재사용성을 높이고 생산성과 유지보수에 크게 기여한다.

* 용어

Parent : 조상 클래스, 부모 클래스, 상위 클래스, 기반 클래스

Child : 자식 클래스, 하위 클래스, 파생 클래스

2. 상속 관계도 (상속 계층도)

 

class Child extends Parent {}
  • Child 클래스는 Parent 클래스의 멤버 들을 모두 포함하고 있다.
  • Parent 클래스에 age라는 변수를 추가 하면 Child 클래스도 자동으로 age라는 변수를 추가하는 것과 같은 효과가 있다.
  • 위와 반대로 Child 클래스에 play() 메서드를 추가하면 Parent 클래스에는 영향이 없다.
  • Parent 클래스를 확장한다고 표현한다.
  • 생성자와 초기화 블럭은 상속되지 않고 멤버(변수, 클래스)만 상속된다.
  • Child 클래스는 Parent 클래스보다 멤버의 개수가 같거나 많다.

* 중요

접근 제어자가 private 또는 default인 멤버는 상속이 되지만 Child 클래스에서 접근이 제한된다.

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

멤버 변수로 다른 클래스 인스턴스를 생성하여 포함관계를 맺는다.

class Circle {
    Point p = new Point(); // 포함
    int r;
}

class Point {
    int x;
    int y;
}

4. 클래스 간의 관계 결정하기

상속 관계 : ~은 ~이다. (is ~ a)

포함 관계 : ~은 ~을 가지고 있다. (has ~ a)

// 상속 관계
class SportsCar extends Car {}

// 포함 관계
class Circle {
    Point p = new Point();
}
  • 가능한 많은 관계를 맺어주어 코드의 재사용성을 높여야 한다.
  • Child, Parent의 이름이 같은 메서드는 Child 메서드가 실행되며, 이것을 '오버라이딩'이라고 한다.
  • 참조 변수를 출력하면 참조 변수가 가리키고 있는 인스턴스의 toString을 호출하여 출력한다.
    모든 클래스의 최상위인 Object클래스에 정의된 toString()을 호출하는 것이 가능하다.
    ex) Card c = new Card();
    System.out.println(c); === System.out.println(c.toString); 두 개의 예제는 동일하다.

5. 단일 상속 (single inheritance)

단일 상속은 하위 클래스가 상위 클래스를 한 개만 상속받을 수 있다는 뜻이다.

여러 개의 클래스로부터 상속받을 수 없으며 여러 상위 클래스의 멤버 이름이 겹치면 구분 방법이 없기 때문이다.

해결 방안으로는 TV 클래스를 상속받고 VCR 클래스를 포함시킬 수 있다.

// 상속과 포함
class TVCR extends TV {
    VCR vcr = new VCR();
}

// 여러 상위 클래스 상속 불가
class TVCR extends TV, VCR {
    ...
}

6. Object 클래스 - 모든 클래스의 조상

다른 클래스로부터 상속받지 않는 클래스들은 모두 자동으로 Object 클래스를 상속받는다. 컴파일러가 추가해준다.

상속 클래스가 있더라도 상위로 계속 올라가면 최상위에는 Object 클래스를 상속받는다.

상속 계층도에서도 Object가 생략되는 경우가 많다.

7. 오버라이딩 (Overriding)

오버라이딩이란?

상속받은 메서드의 내용을 변경하는 것이 오버라이딩이다.

class Parent {
    public int get() {}
}

class Child extends Parent {
    public int get() {} // 오버라이딩
}

Parent 클래스의 get메서드와 비슷한 기능을 기대할 수 있어 오버라이딩을 사용한다.

7.1. 오버라이딩의 조건

  1. 하위 클래스와 상위 클래스의 메서드 명이 같아야 한다.
  2. 하위 클래스와 상위 클래스의 메서드 매개변수가 같아야 한다.
  3. 하위 클래스와 상위 클래스의 메서드 반환 타입이 같아야 한다.

7.2. 제한된 조건하에 변경 가능한 것

  1. 접근 제어자는 상위 클래스의 메서드 보다 좁은 범위로 변경 불가능하지만 넓은 범위로 변경 가능하다.
    범위 순서 : public > protected > default > private
  2. 상위 클래스의 메서드 보다 많은 예외를 선언할 수 없다.
    상위 메서드 : public int get() throws IOException, SQL.... {}
    하위 메서드 : public int get() throws IOException {}

7.3. 오버라이딩의 제한

  1. 접근 제어자를 상위 클래스의 메서드 보다 좁은 범위로 변경 불가능하다.
  2. 예외는 상위 클래스의 메서드 보다 많이 선언 불가능하다.
  3. 인스턴스 메서드를 static 메서드로, static 메서드를 인스턴스 메서드로 변경 불가능하다.

* 중요

상위 클래스에 정의된 static 메서드를 하위 클래스에 똑같은 선어부로 정의 가능하다. 이것은 오버라이딩이 아닌 각 클래스의 static 메서드이다.

8. Super

  • 상위 클래스의 멤버를 참조하는 참조 변수이다.
  • 인스턴스 메서드에서만 사용 가능하다.
  • 상위, 하위 클래스의 멤버가 같아 혼동이 올 때 사용하는 것이 좋다.
  • 상위 클래스의 멤버도 this를 이용해 사용할 수 있다.
  • 오버라이딩 된 메서드에서 상위 메서드 호출시 사용한다.
  • 오버라이딩 된 메서드에서 상위 메서드를 포함시키는 것이 좋다.
    - 추후 업데이트가 돼도 동작하기 때문
    - 상위 메서드에서 추가 작업이 필요할 경우
class Parent {
    public String name;
    
    public int get() {}
}

class Child {
    Child() {
        super(); // 상위 클래스 생성자 호출
        super.name; // 상위 클래스 멤버 변수
        super.get(); // 상위 클래스 메서드 호출
        
    }
    public int get() {]
}

9. Super() - 상위 클래스의 생성자

  • this()와 마찬가지로 생성자이다.
  • 상위 클래스의 멤버 변수도 초기화가 필요하여 사용한다.
  • Object 클래스를 제외하고는 모두 생성자를 첫 줄에 정의한다.
  • 생성자 첫줄에 상위 클래스의 생성자인 super나 자기 자신의 생성자 this가 없으면 컴파일러가 자동으로 추가한다.
  • 상위 클래스의 멤버 변수는 상위 클래스의 생성자를 통해 초기화가 필요하다.
class Parent {
    public String name;
    
    Parent(String name) {
        this.name = name;
    }
    
    public int get() {}
}

class Child {
    Child() {
        super("홍길동"); // 상위 클래스 생성자 호출        
    }
    
    public int get() {]
}

'개발 언어 > Java' 카테고리의 다른 글

[Java] 제어자  (0) 2021.08.25
[Java] Package와 import  (0) 2021.08.24
[Java] 변수의 초기화  (0) 2021.08.24
[Java] 생성자  (0) 2021.08.24
[Java] 오버로딩과 오버라이딩  (0) 2021.08.24

+ Recent posts