JAVA/정리한 것

[ JAVA ] Abstract 와 Interface

따갓 2022. 5. 15. 21:10

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 간에 호환성이 생긴다. ( 인스턴스만 바꾸면 나머진 바꿀 필요 없다.) 

인터페이스를 활용하는 코드에서 벌어지는 단점들.

  1.  모든 클래스( 자동차 : Spark , Colorado ... ) 에서 구현 가능한 형태로 선언해야 한다. 여기에 빠진 기능은 시스템에서도 빠지게 된다.
  2.  한번 인터페이스가 정해지면 고치기 난감하다. 상속받은 모든 클래스 ( 자동차 ) 들도 다 고쳐져야 한다. ( 예를 들어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