abstract( 추상 ) : 인스턴스를 만들 수 없는 클래스
추상 클래스는 상속을 강제하기 위한 것이다. 즉 부모 클래스에는 메소드의 시그니처만 정의해놓고 그 메소드의 실제 동작 방법은 이 메소드를 상속 받은 하위 클래스의 책임으로 위임하고 있다.
abstract class Temp {
abstract public void print(); //abstract method
}
//class Temp2 extends Temp{} << Temp로부터 abstract 메서드를 상속받았으므로 이 클래스도 abstract 로 선언해줘야 오류가 안생김
class Temp2 extends Temp{ //오버라이딩 하면 abstract가 떨어져나간다. 이 코드는 정상작동
public void print(){
System.out.println("HelloWorld");
}
}
public class Main {
public static void main( String[] args ){
//Temp t = new Temp(); << 에러
Tempt t = new Temp2(); // 가능
t.print() // 오버라이딩되어 System.out.println("HelloWorld");이 동작한다.
}
}
abstract method : 선언되었지만 정의되지 않은 메소드. 이걸 가진 클래스는 반드시 abstract로 선언되어야 한다.
오버라이딩 하면 abstract가 떨어져나간다.
interface( 인터페이스 )
>>다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스. 즉 인터페이스는 클래스간의 호환성을 만드는 용도.
자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가지게 될 것이다. 하지만 자바에서는 클래스를 통한 다중 상속은 지원하지 않는다. 대신 자바에서는 인터페이스라는 것을 통해 다중 상속을 지원하고 있다.
interface ITemp{
abstract public void print();
public void print( int i ) ; //abstract 명시안해도 메소드에 자동으로 붙는다.
}
class Temp implements ITemp{ // interface 상속시 extends가 아닌 implements 사용
public void print() { System.out.println("print"); }
public void print( int i ) { System.out.println("print2"); };
}
public class Main{
public static void main(String[] args){
//new ITemp(); << 에러
ITemp t = new Temp();
t.print();
t.print( 100 );
}
}
- interface는 abstract public 한 메서드만 멤버로 가질 수 있다.( 일단은 이렇게 알아두자 )
- 매서드에 abstract 안붙여도 자동으로 붙는다.
- 상속해서 클래스 선언할 때 extends 가 아닌 implements 사용한다. 이 때 모든 메서드를 다 오버라이딩 해야한다. ( 안해주면 ? abstract class )
- "조상클래스의 참조형변수 = 자손클래스의 인스턴스" [A t = new B()] 개념은 동일하게 적용된다.
interface를 활용하는 코드 예시
interface ICar{
public void turnRight();
public void turnLeft();
}
class Spark implements ICar{
public void turnRight(){ System.out.println("tR"); }
public void turnLeft(){ System.out.println("tL"); }
}
class Colorado implements ICar{
public void turnRight(){ System.out.println("turnR"); }
public void turnLeft(){ System.out.println("turnL"); }
}
public class Main{
public static void main(String[] args){
ICar car = new Colorado(); // new Spark(); 로 바꾸기만 하면 됨.
car.turnRight();
car.turnLeft();
}
}
- 인터페이스는 클래스간의 호환성을 만드는 용도.
Spark <-> Colorado 간에 호환성이 생긴다. ( 인스턴스만 바꾸면 나머진 바꿀 필요 없다.)
인터페이스를 활용하는 코드에서 벌어지는 단점들.
- 모든 클래스( 자동차 : Spark , Colorado ... ) 에서 구현 가능한 형태로 선언해야 한다. 여기에 빠진 기능은 시스템에서도 빠지게 된다.
- 한번 인터페이스가 정해지면 고치기 난감하다. 상속받은 모든 클래스 ( 자동차 ) 들도 다 고쳐져야 한다. ( 예를 들어turnRight -> turnRight2 이런식으로 메서드 이름이 수정될 경우 )
그래서 많은 사람들이 동시에 개발하는 경우에 인터페이스를 활용하여 일을 분배하는 경우가 많다.
[ Command Pattern ]
interface ICalc {
public int execute( int i );
}
class Plus implements ICalc{
private int data = 0;
public Plus( int i ){
this.data = i;
}
public int execute( int i ){return this.data + i;}
}
class Minus implements ICalc{
private int data = 0;
public Minus( int i ){
this.data = i;
}
public int execute( int i ){ return i - data ; } //this 생략
}
class Multi implements ICalc{
private int data = 0;
public Multi( int i ){
this.data = i;
}
public int execute( int i ){ return i * data ; } //this 생략
}
public class Main{
public static void main(String[] args){
ICalc ic = new Plus( 5 );
System.out.println( ic.execute( 3 ) ); // 5 + 3의 결과 출력
ICalc ic2 = new Minus( 2 );
System.out.println( ic2.execute( 3 ) ); // 3 - 2의 결과
ICalc[] l = new ICalc[4];
l[0] = new Plus( 3 ); // ICalc l[0] = new Plus(3);
l[1] = new Minus( 1 );
l[2] = new Plus( 4 );
l[3] = new Minus( 3 );
int start = 10;
for( int i = 0 ; i < l.length ; i++){
start = l[i].execute( start );
}
System.out.println(": " + start );
}
}
- 동작 하나를 인스턴스로 만들어서 미리 일련 작업을 저장해 놓으면 필요할 때마다 반복문 돌려서 한꺼번에 실행할 수 있다.
- 동작 하나를 인스턴스로 만들어서 활용하는 기법을 Command Pattern 이라고 한다. (유명한 객체지향 설계기법)
[ Decorator Pattern ]
- 객체의 결합 을 통해 기능을 동적으로 유연하게 확장 할 수 있게 해주는 패턴
- 기본 기능에 추가할 수 있는 기능의 종류가 많은 경우에 각 추가 기능을 Decorator 클래스로 정의 한 후 필요한 Decorator 객체를 조합함으로써 추가 기능의 조합을 설계 하는 방식이다.
interface IGreet{
public String greet();
}
class HelloGreet implements IGreet{
public String greet(){ return "Hello"; }
}
class MerciGreet implements IGreet{
public String greet(){ return "Merci"; }
}
class SharpDeco implements IGreet{ // 문자열 양옆에 # 을 붙여주는 기능
private IGreet ig = null;
public SharpDeco(IGreet i){
this.ig = i;
}
public String greet(){
return "#" + ig.greet() + "#"; // 문자열 양옆에 # 을 붙여주는 기능
}
}
class StarDeco implements IGreet{ // 문자열 양옆에 * 을 붙여주는 기능
private IGreet ig = null;
public StarDeco(IGreet i){
this.ig = i;
}
public String greet(){
return "*" + ig.greet() + "*" ; // 문자열 양옆에 * 을 붙여주는 기능
}
}
public class Main{
public static void main(String[] args){
IGreet ig1 = new SharpDeco(new MerciGreet());
IGreet ig2 = new StarDeco(new SharpDeco(new HelloGreet()));
IGreet ig3 = new SharpDeco(new StarDeco(new HelloGreet()));
System.out.println( ig1.greet() ); // 결과 : #Merci#
System.out.println( ig2.greet() ); // 결과 : *#Hello#*
System.out.println( ig3.greet() ); // 결과 : #*Hello*#
}
}
[ 참고 ]
https://gmlwjd9405.github.io/2018/07/09/decorator-pattern.html
'JAVA > 정리한 것' 카테고리의 다른 글
| [ JAVA ] 익명 클래스 ( Anonymous Class ) (0) | 2022.05.16 |
|---|---|
| [ JAVA ] 제너릭( Generic ) (0) | 2022.05.16 |
| [ JAVA ] 객체지향언어의 특징 - 캡슐화 (0) | 2022.05.15 |
| [ JAVA ] 객체지향언어의 특징 - 다형성 (0) | 2022.05.14 |
| [ JAVA ] 객체지향언어의 특징 - 상속성 (0) | 2022.05.14 |