본문 바로가기
언어의 기초/자바(JAVA)

[JAVA] 클래스 다형성, 상속성 , 추상화 ,업캐스팅/ 다운캐스팅

by 지에스정 2021. 11. 29.


Polymorphism (다형성)

 

 1.  Overloading(오버로딩)

 

같은 이름의 메소드를 넘쳐서 (여러개) 불러오는(선언하는) 기법

매개변수의 개수 혹은 타입이 다르다면 매개변수로 각각을 구별 할 수 있기 때문에 같은 이름의 메소드로 여러개 선언할 수 있다

오버로딩된 메소드를 사용할 시 전달된 값의 타입 혹은 개수로 구분하여 알맞은 메소드가 자동으로 호출된다

 

 2.  Overriding(오버라이딩) : 재정의

부모 필드에 a( ) 라는 메소드가 존재한다면 자식 객체 생성 시 부모 필드가 먼저 메모리에 올라가게 되므로 부모 a ()가 메모리에 먼저 존재한다

그 다음 자식 필드를 완성 시킬때 똑같은 이름의 a()라는 메소드 가 존재 한다면 같은 이름으로 2개가 만들어 지는 것이 아니라 기존에 만들어진 부모 필드의 a()의 메소드 대신에 자식에서 작성한 내용이 사용된다

(부모 필드에 있는 a() 자식에서 작성한 내용이 덮어씌어진다)

그러므로 자식객체 a()메소드를 사용하게 되면 재정의된 기능으로 사용하며 이것을 오버라이딩이라고 한다

 

클래스 배열

 

객체를 여러개 선언해야 하는 경우 배열 타입으로 한번에 선언 후 사용

각 객체는 규칙성이 없기 때문에 규칙성을 부여하기 위해 사용

한칸 한칸이 하나의 객체

 

클래스 배열 선언

 

클래스명[] 배열명 = {
	new 생성자(),
	new 생성자(),
	new 생성자(),
	...
}
클래스명[] 배열명 = new 클래스명[칸수];

클래스 배열 사용

 

각 칸이 객체이기 때문에 각 방으로 접근 후 ( . )을 찍고 필드로 접근한다

 

배열명[idx].변수 ;
배열명[idx].메소드() ;

 


상속(inheritance)

 

1. 기존에 선언된 클래스의 필드를 다른 클래스에서도 사용하고자 할 때

2. 클래스들을 만들다 보니 공통요소가 보여서 부모로 묶은 후 상속시켜 주어서 클래스를 편하게 만들고자 할 때

 

상속방법

 

class A {
	A의 필드
}
class B extends A{
	A 필드 + B 필드
}
B obj = new B();

obj 객체는 실제로 A의 필드와 B의 필드 둘다 가지고 있다

 

A : 부모 클래스 , 상위 클래스, 슈퍼 클래스, 기반 클래스

 B : 자식 클래스 , 하위 클래스 ,서브 클래스, 파생 클래스

 

자식 클래스의 생성자 호출시 일어나는 일

 

자식 클래스의 객체는 자식 생성자를 호출하여 객체화를 진행한다

자식 생성자를 호출 했으므로 자식 클래스 내의 필드들이 똑같이 복사되어 하나 생성된다.

만약 자식 클래스 의 필드만 메모리에 올라갔다면 자식 객체로 절대 부모 클래스 내부에 있는 필드에는 접근할 수 없다.

 따라서 자식 생성자를 호출하게 되면 부모 필드가 먼저 메모리에 할당되고 그 이후 자식 필드도 따라 붙는다

결과적으로 자식 객체는 부모 필드가 메모리에 같이 올라와 있기 때문에 접근할 수 있게 된다

 

자식 생성자 첫 줄에는 항상 super클래스의 생성자가 호출된다

명시적으로 적지 않을시에는 보이지 않지만 super() 호출

 

전역 변수 : 각 객체마다 하나씩 필드로 존재하는 변수들

 


 static

 

static 이 붙은 변수, 메소드 , 구역 등은 프로그램 실행시 가장 먼저 메모리에 올라간다

static 이 붙은 메소드 내부에서는 객체의 필드에 올라오는 일반 전역변수나 일반 메소드는 사용할 수 없다 (아직 메모리에 올라오지 않았기 때문에)

따라서 static 붙은 메소드, 자기 자신 안에 선언된 지역변수만 사용할 수 있다

 

static 이 붙은 전역변수, 메소드는 모든 객체가 굥유한다

따라서 static 필드는 클래스 이름으로 직접 접근이 가능하다

실제로 공유해야 하는 값, 메소드 또는 필드가 필요없는 메소드 경우에는 static을 붙여서 선언한다

(공유하지 않아야 하는 값들에는 무조건 static을 붙이면 안된다)

 


업 캐스팅(Up casting)

 

자식 타입의 객체를 부모 타입으로 변환

부모 타입의 객체에 자식 클래스의 필드를 담아주는 것

SuperCar supercar = new SuperCar();
Car car = supercar;

부모 타입의 객체에 자식 생성자를 호출

Car car = new SuperCar();

 

부모 타입의 매개변수에 자식 객체를 넘길 떄

void f(Car car){
}
 f(new SuperCAr());

업캐스팅 된 객체는 자식 클래스에서 추가한 내용을 사용할 수 없다

단, 오버라이딩 된 메소드는 사용 가능하다

 

다운 캐스팅(Down casting)

 

업캐스팅 된 객체를 다시 자식타입으로 바꾸는 것

업캐스팅 시 잘려 나갔던 자식클래스의 추가된 내용을 다시 달아 주는 것

 

(자식클래스)업캐스팅된 객체;

 

instanceof

 

객체 instanceof 클래스명 : 객체가 뒤에오는 클래스 타입인지 비교

true / false 두가지 결과로 나오는 조건식

 

부모객체 instanceof 부모클래스 : true

부모객체 instanceof 자식클래스 : false

자식객체 instanceof 부모클래스 : true

자식객체 instanceof 자식클래스 : true

업캐스팅객체 instanceof 부모클래스 : true

업캐스팅객체 instanceof 자식클래스 : true

 


접근 권한 제어자

 

다른 패키지 혹은 다른 클래스에서 해당 필드에 접근할 수 있는

권한을 제어해주는 , 설정해주는 키워드들

 

(default) : 다른 패키지에서 접근 불가

 public : 모든 곳에서 접근 가능

private : 다른 클래스, 다른 패키지 에서 접근 불가

protected : 다른 패키지에서 접근 불가 / 단 자식은 자신의 필드처럼 접근 가능

 

public - 모든곳에서 자유롭게 접근이 가능해야 하는 필드들 (데이터를 처리하고 기능을 담당하는 메소드들)

private - 데이터를 담는 변수들(게시글 제목, 회원 아이디, ...)

protected - 상속이 목적(자식 클래스들이 오버라이딩 해서 써야하는 메소드)

(default) - 확실하게 같은 패키지에서만 이용할 때, 귀찮을 때

 

private 필드는 직접 접그할 방법이 하나도 없기 떄문에 무조건

public 메소드를 통해서 간접적으로 접근한다

(값을 사용하는 경우 : getter / 수정하는 경우 : setter)

 

추상 클래스 (불완전한 클래스)

 

추상적인 내용만 정의하고 있는 클래스로, 구체적인 내용은 존재하지 않는 클래스

상속받은 하위 클래스에서 그 내용들을 구현하도록 설계된 클래스

추상 클래스(부모)는 클래스들(자식)의 공통 요소들을 추상적으로 가상화 시켜놓그 때문에 객체화 시 자식에서 재정의를 통한 구현을 하고 그 자식 타입으로 객체화를 잰행 해서 사용

부모 추상 클래스의 역할은 단지 자식 클래스를 만들 떄 틀로 존재해서 도와주는 역할을 수행

그렇기 때문에 자식 클래스에서 재정의 해야되는 메소드의 이름과 형태만 가짐

즉 메소드에 {}(바디) 가 없다

이러한 메소드를 추상 메소드라고 하며, abstract라는 키워드가 리턴타입 앞에 붙음

추상 메소드가 하나라도 포함되어 있다면 그 클래스는 추상클래스로 만들어야 한다

 

 추상 클래스 선언

 

abstract class 추상클래스명{
	abstract 리턴타입 추상메소드 명();
}

추상 메소드는 자식 클래스에서 강제적으로 반드시 재정의 해야한다

일반 메소드도 추상 클래스 내부에 존재할 수 있다

 

인터페이스

 

추상 클래스를 고도화 시킨 문법이며, 반드사 추상 메소드와 상수만 선언해야 한다

다른 클래스에서 인터페이스를 지정 받을 때에는 implements라는 키워드 사용

지정받은 클래스는 인터페이스가 가지고 있는 추상 메소드를 반드시 재정의 해야 한다

 

jdk 8 버전 이후로 부터는 인터페이스에 default 메소드를 선언할 수 있다

따라서 사실상의 다중상속을 지원하게 된다

인터페이스 끼리나 다른 클래스와의 메소드 모호성이 발생한다면, (같은 이름의 메소드 끼리 충돌이 발생한다면) 상속 받은 클래스 에서 명시적으로 작성(오버라이딩) 해 주어 모호성을 해결해 주어야 한다

내부에서는 인터페이스명.super.method()로 지정받은 인터페이스에 있는 메소드를 호출할 수 있고 이는 다중상속을 받을 수 있다는 것을 의미한다.

 

내부 클래스(inner class)

 

클래스 내부에 클래스를 선언하며 외부 클래스의 필드에 용이하게 접근하기 위해 사용한다

내부 클래스의 필드를 사용하기 위해서는 먼저 외부 클래스의 객체를 만들고 그 객체를 통해 내부 클래스까지 접근해서 객체화를 해야한다

외부클래스의 필드에서는 자신이랑 같이 올라와 있는 내부 클래스를 바로 객체화 할 수 있다

보통은 어떤 메소드 호출 결과로 내부 객체를 부여받아서 사용하는 방식으로 설계

 

외부클래스명 객체명 = new 외부클래스생성자();
외부클래스명.내부클래스명 객체명 = 외부클래스객체.new 내부클래스생성자();

 

내부 클래스를 상용하는 이유

 

1.  상속처럼 이용

외부클래스의 필드를 마치 내것처럼 접근하여 사용하기 위함

2.  캡슐화

 외부 클래스의 객체가 없다면 내부 클래스도 존재할 수 없기 떄문에

다른 클래스에서 접근하지 못하도록 내부 클래스를 숨기기 위함

 

가정 : B클래스의 b() 메소드가 자주 쓰이고 이 작업은 A클래스의 데이터가 필요

만약 다른 곳에서는 b() 메소드가 전혀 안쓰이거나, B클래스를 외부에 노출시키고 싶지않거나, B클래스의 객체가 있기 전에 필수적으로 A클래스의 객체가 있어야 한다면 설계를 내부 클래스 형태로 만들어서 사용