OOP의 4대 특성 혹은 객체지향의 4대 특성, 4대 원칙이라고 불린다.
객체지향 프로그래밍의 특성으로 객체지향을 객체지향에 답게 만들어주는 원칙이라고 보면 될 것 같다. 또한 4가지 특성은 궁극적으로 재사용성 증진과 유지보수의 효율성을 보장하고자 한다.
캡슐화란 객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것을 말한다.
외부 객체는 내부의 구조를 알지 못하며 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있다.
필드와 메소드를 캡슐화하여 보호한 이유는 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 하기 위함이다.
기존에는 코드에서 변수와 함수를 재활용하기에는 분산되어 있기 떄문에 재활용이 어려웠는데, 이를 캡슐화를 통해 관련된 기능과 특성을 한 곳에 모으고 분류하기 때문에 재활용이 원활하도록 해결했다.
접근제한자 같은 경우 캡슐화에 해당한다.
접근 제한자 표기법
객체지향 프로그래밍에서는 부모역할의 상위 객체와 자식역할의 하위 객체가 존재한다.
상속(inheritance)이란 상위 객체는 자기가 가지고 있는 필드와 메소드를 하위 객체에게 물려주어 하위 객체가 사용할 수 있도록 돕는다.
또는 기존의 클래스에 기능을 추가하거나 재정의하여 새로운 클래스를 정의하는 것을 의미한다.
즉, 재사용성과 확장성을 보장하기 위해 사용된다.
상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아, 새로운 클래스를 생성할 수 있다.
이때 기존에 정의되어 있던 클래스를 부모 클래스(parent class) 또는 상위 클래스(super class), 기초 클래스(base class)라고도 한다.
그리고 상속을 통해 새롭게 작성되는 클래스를 자식 클래스(child class) 또는 하위 클래스(sub class), 파생 클래스(derived class)라고도 한다.
-> 상위클래스는 물려줄 특성이 풍성할 수록 / 인터페이스는 구현을 강제할 method 갯수가 적을 수록 좋다
= LSP 리스코프치환 법칙 / ISP 인터페이스 분할 원칙
자바에서 클래스의 상속은 다음과 같은 장점을 갖는다.
기존에 작성된 클래스를 재사용 가능하다.
자식 클래스 설계 시 중복되는 멤버를 미리 부모 클래스에 작성해 놓으면, 자식 클래스에서는 해당 멤버를 작성하지 않아도 되므로 반복된 코드 중복을 줄여준다.
클래스 간의 계층적 관계를 구성함으로써 다형성의 문법적 토대를 마련한다.
자식 클래스(child class)
자식 클래스(child class)란 부모 클래스의 모든 특성을 물려받아 새롭게 작성된 클래스를 의미한다.
자바에서는 부모클래스와 자식클래스는 아래와 같은 구조를 갖는다.
이처럼 부모 클래스는 자식 클래스에 포함된 것으로 볼 수 있다.
따라서 부모 클래스에 새로운 필드를 하나 추가하면, 자식 클래스에서의 선언이 이루어진다면, 자동으로 해당 필드가 추가된 것처럼 동작한다.
자식 클래스에는 부모 클래스의 필드와 메소드만이 상속되며, 생성자와 초기화 블록은 상속되지 않는다.
또한, 부모 클래스의 접근 제어가 private이나 default로 설정된 멤버는 자식 클래스에서 상속받지만 접근할 수는 없다.
class Parent {
private int a = 10; // private 필드
public int b = 20; // public 필드
}
class Child extends Parent {
public int c = 30; // public 필드
void display() {
//1. System.out.println(a); // 상속받은 private 필드 참조
//2. System.out.println(b); // 상속받은 public 필드 참조
//3. System.out.println(c); // 자식 클래스에서 선언한 public 필드 참조
}
}
public class Inheritance01 {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}
위 예제에서.
1번 라인에서는 부모 클래스에서 상속을 받으려는 시도를 했지만, private 키워드의 접근 제어 때문에 값을 받아올 수 없다.
2번 라인에서는 자식 클래스의 메소드에서 부모 클래스에서 상속받은 public 필드를 참조하고 있다.
이처럼 자식 클래스에서 따로 선언하지 않은 필드라도 해당 이름의 필드를 부모 클래스에서 상속받았다면 문제가 없습니다.
3번 라인에서는 자신만의 필드나 메소드를 선언하여 사용한 것이기 때문에 값을 사용할 수 있다.
Object 클래스
자바에서 Object 클래스는 모든 클래스의 부모 클래스가 되는 클래스.
따라서 자바의 모든 클래스는 자동으로 Object 클래스의 모든 필드와 메소드를 상속받게 된다.
즉, 자바의 모든 클래스는 별도로 extends 키워드를 사용하여 Object 클래스의 상속을 명시하지 않아도 Object 클래스의 모든 멤버를 자유롭게 사용할 수 있다.
자바의 모든 객체에서 toString()이나 clone()과 같은 메소드를 바로 사용할 수 있는 이유가 해당 메소드들이 Object 클래스의 메소드이기 때문이다.
추상화란 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 공통의 속성이나 기능을 묶어 이름을 붙이는 것이다.
자료의 추상화: 객체지향 관점에서 클래스를 정의하는 것이다.
추상클래스: 여러 클래스간 비슷한 필드와 메서드를 공통적으로 추출해 만들어진 클래스이다.
인터페이스: 동일한 목적 하에 동일한 기능을 수행하게끔 강제하는 것이다.
추상 클래스 : 여러 클래스간 비슷한 필드와 메서드를 공통적으로 추출해 만들어진 클래스
추상 클래스는 필드, 생성자, 추상메서드를 가질 수 있고, 인터페이스와 다르게 필드도 가질 수 있다.
추상 클래스는 하위 클래스들의 공통점들을 추상화하여 만든 클래스이기 때문에 추상 클래스 자체를 new 연산자를 통하여 객체를 생성하는 것은 불가능하다.
인터페이스 : 동일한 목적 하에 동일한 기능을 수행하게끔 제어하는 것
인터페이스는 상수(static final)와 추상 메서드(abstract method)의 집합이다.
또한 생성자를 가질 수 없으며, 따라서 객체화가 불가능(= 로직 작성이 불가능)하다.
인터페이스에 선언된 상수와 추상 메서드는 public static final 과 public abstract 를 생략해도 되는데, 이유는 컴파일 시에 자동으로 생성해주기 때문이다.
인터페이스는 다중상속을 지원하며, 구현체에 여러개의 인터페이스를 구현할 수 있다.
구현체란 인터페이스를 구현한 클래스라는 뜻이며, 구현 클래스 혹은 실체 클래스 라고도 부른다.
공통점 : 자기 자신이 객체를 생성할 수 없으며 상속받은 자식만이 객체를 생성할 수 있고,
선언만 있고 구현 내용이 존재하지 않는다.
대표적인 3가지를 정리하면 아래와 같다.
목적
추상클래스 : 상속받아 기능을 이용하고, 확장시키기 위해 사용한다.
인터페이스 : 구현 객체의 중복 동작을 보장하기 위해 사용한다.
형태
추상클래스 : 일반변수 + 일반메소드 + 추상메소드
인터페이스 : 상수 + 추상메소드
사용 키워드
추상클래스 : extends
인터페이스 : implements
다형성(polymorphism)이란 하나의 객체가 여러 가지 타입을 가질 수 있는 것을 의미한다. 또는 같은 타입이지만 실행 결과가 다양한 객체를 이용할 수 있는 성질을 말한다.
자바에서는 이러한 다형성을 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하여 구현하고 있다.
다형성의 효과로는 객체는 부품화가 가능하다. 예를 들어 자동차를 설계할 때 타이어 인터페이스 타입을 적용했다면 이 인터페이스를 구현한 실제 타이어들은 어떤 것이든 상관없이 장착이 가능하다.
참조 변수의 다형성
자바에서는 다형성을 위해 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하고 있습니다.
이때 참조 변수가 사용할 수 있는 멤버의 개수가 실제 인스턴스의 멤버 개수보다 같거나 적어야 참조할 수 있다.
위에서 언급된 인스턴스란? 일반적으로 실행 중인 임의의 프로세스, 클래스의 현재 생성된 오브젝트를 가리키고, 시스템 자원의 접근에 할당된 물리 메모리 일부를 가리킨다.
즉 "클래스로 인해 생성된 하나의 객체"라고 보면 된다.
class Parent { ... }
class Child extends Parent { ... }
...
Parent pa = new Parent(); // 허용
Child ch = new Child(); // 허용
Parent pc = new Child(); // 허용
Child cp = new Parent(); // 오류 발생.
특정 타입의 참조 변수로는 당연히 같은 타입의 인스턴스를 참조할 수 있다.
그리고 부모 클래스 타입의 참조 변수로도 자식 클래스 타입의 인스턴스를 참조할 수 있다.
하지만 반대의 경우인 자식 클래스 타입의 참조 변수로는 부모 클래스 타입의 인스턴스를 참조할 수 없다.
참조 변수의 타입 변환
자바에서는 참조 변수도 다음과 같은 조건에 따라 타입 변환을 할 수 있다.
서로 상속 관계에 있는 클래스 사이에만 타입 변환을 할 수 있다.
자식 클래스 타입에서 부모 클래스 타입으로의 타입 변환은 생략할 수 있다.
하지만 부모 클래스 타입에서 자식 클래스 타입으로의 타입 변환은 반드시 명시해야 한다.
참조 변수의 타입 변환도 기본 타입의 타입 변환과 마찬가지로 타입 캐스트 연산자(())를 사용합니다.
class Parent { ... }
class Child extends Parent { ... }
class Brother extends Parent { ... }
...
Parent pa01 = null;
Child ch = new Child();
Parent pa02 = new Parent();
Brother br = null;
pa01 = ch; // pa01 = (Parent)ch; 와 같으며, 타입 변환을 생략할 수 있음.
br = (Brother)pa02; // 타입 변환을 생략할 수 없음.
br = (Brother)ch; // 직접적인 상속 관계가 아니므로, 오류 발생.
자바에서 다형성을 지원하는 방법으로 메서드 오버로딩과 오버라이딩
서로의 개념은 차이가 있지만, 비슷한 이름때문에 혼동이 잦다.
오버로딩(Overloading) : 하나의 객체에서 이름이 같은 메소드를 여러개 정의하여 사용하는 것
class OverloadingTest{
//이름이 cat인 메서드
void cat(){
System.out.println("매개변수 없음");
}
//매개변수 int형이 2개인 cat 메서드
void cat(int a, int b){
System.out.println("매개변수 :"+a+", "+b);
}
//매개변수 String형이 한 개인 cat 메서드
void cat(String c){
System.out.println("매개변수 : "+ c);
}
}
public class OverTest {
public static void main(String[] args) {
//OverloadingTest 객체 생성
OverloadingTest ot = new OverloadingTest();
//매개변수가 없는 cat() 호출
ot.cat();
//매개변수가 int형 두개인 cat() 호출
ot.cat(20, 80);
//매개변수가 String 한개인 cat() 호출
ot.cat("오버로딩 예제입니다.");
}
}
오버라이딩(Overriding) : 상위 클래스가 가지고 있는 메서드를 하위 클래스가 재정의해서 덮어쓰기 사용
즉, 메서드의 이름이 서로 같고, 매개변수가 같고, 반환형이 같을 경우에 상속받은 메서드를 덮어쓴다고 생각하면된다. '부모클래스의 메서드는 무시하고, 자식 클래스의 메서드 기능을 사용하겠다'와 같다.
class Woman{ //부모클래스
public String name;
public int age;
//info 메서드
public void info(){
System.out.println("여자의 이름은 "+name+", 나이는 "+age+"살입니다.");
}
}
class Job extends Woman{ //Woman클래스(부모클래스)를 상속받음 :
String job;
public void info() {//부모(Woman)클래스에 있는 info()메서드를 재정의
super.info(); //super.()는 부모클래스의 생성자를 호출할 때 사용
System.out.println("여자의 직업은 "+job+"입니다.");
}
}
public class OverTest {
public static void main(String[] args) {
//Job 객체 생성
Job job = new Job();
//변수 설정
job.name = "유리";
job.age = 30;
job.job = "프로그래머";
//호출
job.info();
}
}
오버로딩과 오버라이딩의 성립조건
자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말한다.
강제 타입 변환은 부모 타입을 자식 타입으로 변환하는 것을 말한다.
하지만 모든 부모 타입을 자식 클래스 타입으로 강제 변환할 수 있는 것은 아니다. 자식 타입이 부모 타입으로 자동 변환한 후 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용할 수 있다.
변환 가능한 상태여부를 객체 타입 확인(Instanseof)라고 한다.
References (참고 자료)