[211115] 교육 15일차

oxllz·2022년 1월 28일
0

교육

목록 보기
11/41

다중상속

class A {}
class B {}
// 대를 거쳐가며 상속받는 것은 다중상속이 아니다.
class D extends A {}
class E extends D {}
//
interface IA {}
interface IB {}
// 인터페이스 상속해서 인터페이스 선언시 extends , 다중상속 가능
interface IC extends IA, IB {}
// 인터페이스 상속해서 class 선언시 implements , 다중상속 가능
class F implements IA, IB {}
class G extends B implements IA, IB {}
//
public class Test107 {
	public static void main( String[] args ) {
    	 //...
	}
}

인터페이스는 다중상속을 지원한다


인터페이스의 메서드들은 선언되었지만 정의되지 않기 때문에 함수 포인터가 동시에 두개의 함수 선언을 가리키는 일은 발생하지 않는다.


Command Pattern

interface ICalc {
	public int execute( int i );
}
//
class Plus implements ICalc {
	private int data = 0;
	public Plus( int j ) {
		this.data = j;
	}
	public int execute( int i ) { 
		return i + data; 
	}
}
//
class Minus implements ICalc {
	private int data = 0;
	public Minus( int j ) {
		this.data = j;
	}
	public int execute( int i ) {
		return i - data; 
	}
}
//
class Multi implements ICalc {
	private int data = 0;
	public Multi( int j ) {
		this.data = j;
	}
	public int execute( int i ) {
		return i * data; 
	}
}
//
public class Test109 {
	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 ) );
		//
		ICalc[] l = new ICalc[4];
		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

interface IGreet {
	public String greet();
}
// Game Player 
class HelloGreet implements IGreet {
	public String greet() { return "Hello"; }
}
//
class MerciGreet implements IGreet {
	public String greet() { return "Merci"; }	
}
// 과금 Item
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() + "*";
	}
}
//
class DoubleDeco implements IGreet {
	private IGreet ig = null;
	public DoubleDeco( IGreet i ) {
		this.ig = i;
	}	
	public String greet() {
		return ig.greet() + ":" + ig.greet();
	}
}
//
public class Test110 {
	public static void main( String[] args ) {
		IGreet ig = new SharpDeco( new DoubleDeco( new MerciGreet() ) );
		System.out.println( ig.greet() ); // #:Merci:#
	}
}

HelloGreet 과 MerciGreet 를 플레이어의 게임 캐릭터라고 생각하면 SharpDeco, StarDeco, DoubleDeco 를 과금 아이템이라고 생각하면 이해하기 편하다.
예를 들어 닉네임을 꾸미고 싶으면 아이템을 구매해 닉네임에 적용하는 것이다. ( 원하는 만큼!! )


Wrapper Class

public class Test111 {
	public static void main( String[] args ) {
		Object t = 100;
		System.out.println( t.getClass().getName() ); // java.lang.Integer
		//
		Integer t2 = new Integer( 200 );
		Integer t3 = t2;
		// 이런 경우 ( Integer 형 변수를 int 변수에 대입하는 ) 
		// 컴파일러는 자동으로 t3.intValue() 를 호출하게 된다. 이런 것을 Unboxing 이라고 한다.
		int j = t3;		
		System.out.println( j );	
		//
		int k = t;	// Integer 형 참조형 변수는 Unboxing 되지만 Object 형 변수는 Unboxing 안된다
	}
}

java 는 int* , double* 같은 자료형 변수의 기억공간에 대한 포인터 개념이 아예 없다. 그 대안으로 제시한 것이 Wrapper Class

int - Integer
double - Double
float - Float
char - Character
boolean - Boolean 

Object t = 100; : t 는 인스턴스를 가리키기 위한 용도의 참조형변수. 100 은 정수값

t.getClass().getName() : t 가 가리키는 인스턴스를 생성한 클래스명

Auto Boxing

Object t = 100; 이런 코드가 보여지면 컴파일러는
Object t = new Integer(100); 이렇게 바꿔(씌워)준다.

"참조형 변수에 값을 대입해야 하는 코드가 보이면 그때는 값을 Wrapping 해 준다"

Unboxing

int j = new Integer( 200 );
Integer 형 변수를 int 형 변수에 대입하는 경우 .intValue() 를 호출한다.
Integer 형 변수는 언박싱이 가능하고, Object 형 변수는 불가능하다.


가변길이 파라미터

class Temp {
	public void print( String... p ) {
		System.out.println( p.length );
		for( int i = 0 ; i < p.length ; i++ ) {
			System.out.println( p[i] );
		}
		System.out.println();
	}
	public void print2( Object... p ) {
		System.out.println("print2");
	}
}
//
public class Test113 {
	public static void main( String[] args ) {
		Temp t = new Temp();
		t.print();
		t.print("apple");
		t.print("apple","banana");
		t.print("apple","banana","orange");
		// 매개변수를 어떻게 넣더라도 AutoBoxing 때문에 다 가능한 함수가 되어버린다.
		t.print2( 100, 3.14 );
		t.print2( 100, 3.14, "HelloWorld" , null );
	}
}

String... p 가변길이 파라미터 라고 한다. 정체는 배열이다.
매개변수에 전달되는 것이 String 이기만 하면 갯수는 상관없는 형태가 된다.


제네릭

class Bank {
	Object t = null;
}
class Bank2 {
	String t = null;
}
public class Test114 {
	public static void main( String[] args ) {
		Bank b = new Bank();
		b.t = "HelloWorld";
		// 에러 : String b2 = b.t;
		String b2 = (String)b.t;
		//
		Bank2 c = new Bank2();
		c.t = "HelloWorld";
		String c2 = c.t;
	}
}


Bank는 어떤 인스턴스이든 멤버변수로 가리킬 수 있지만, 원래대로 꺼낼때 반드시 원래 타입으로 캐스팅 해 주어야 한다.
Bank2는 String 만 멤버변수를 이용하여 가리킬 수 있지만 , 원래대로 꺼낼때 캐스팅 필요 없다.

양쪽의 장점이 확연히 갈린다. "양쪽의 장점을 결합할 수는 없을까??"

class Bank <X extends Object> {
	X t = null;
}
//
public class Test115 {
	public static void main( String[] args ) {
		Bank<Object> bank = new Bank<Object>();
		bank.t = "HelloWorld";
		String t2 = (String)bank.t;
		//
		Bank<String> bank2 = new Bank<String>();
		bank2.t = "HelloWorld";
		String t3 = bank2.t;
		// 이런식으로 오토박싱 언박싱을 이용하니 코드가 깔끔해진다.
		Bank<Integer> bank3 = new Bank<Integer>();
		bank3.t = 100;
		int j = bank3.t;
	}
}

제네릭 : 클래스 안에서 언급되는 변수의 타입을 <> 안에 지정되는 타입으로 동적으로 결정할 수 있다.

인터페이스에 제네릭을 활용한 예

interface ITemp <X> {
	public X getData();
}
//
class Temp implements ITemp<String> {
	public String getData() { return "HelloWorld"; }
}
//
class Temp2 implements ITemp<Integer> {
	public Integer getData() { return 100; }	
}
//
public class Test117 {
	public static void main( String[] args ) {
		ITemp<String> it = new Temp();
		String l = it.getData();
		System.out.println( l );
		//
		ITemp<Integer> it2 = new Temp2();
		int j = it2.getData();
		System.out.println( j );		
	}
}

Anonymous class

class A {}
//
interface ITemp<X> {
	public X getData();
}
//
public class Test118 {
	public static void main( String[] args ) {
		A t = new A(){};
		System.out.println( t.toString() );
		//
		ITemp<Integer> it = new ITemp<Integer>(){
			public Integer getData(){ return 100; }
		};
		int j = it.getData();
		System.out.println( j );
	}
}

new ITemp<Integer>(){ ... } : ITemp 를 상속받으며 만든 무명의 클래스의 인스턴스 생성. ITemp 를 상속받았으니 부모에서 선언된 메소드를 오버라이딩 해 줘야 한다.

anonymous class : 부모 클래스로부터 상속받으며, 클래스를 선언하는데, 이름이 없다! 재사용이 불가능한 1회용 클래스라는 뜻

interface ITemp<X> {
	public X getData();
}
//
public class Test119 {
	public static void main( String[] args ) {
		int t = 200;
		//
		ITemp<Integer> it = new ITemp<Integer>() {
			public Integer getData(){ 
				//	에러 : t = t + 1;
				return t; 
			}
		};
		int j = it.getData();
		System.out.println( j );
	}
}

로컬변수를 만일 anonymous class 가 사용하고 있다면
해당 인스턴스가 가베지 컬렉션 되지 않는 한에서는 로컬변수는 함수 호출이 끝나도 없어지면 안된다.

  • anonymous 클래스가 사용중인 로컬변수는 함수호출 후에도 계속 유지되어야 한다.
  • anonymous 클래스가 사용중인 로컬변수는 값을 변경해서는 안된다.

Callback

interface ICallback {
	public void onEvent( int i );
}
//
class Button {
	public void onClick( ICallback cb ) {
		System.out.println("onClick ...");
		if( cb != null ) {
			cb.onEvent( 100 );
		}
	}	
}
//
public class Test120 {
	public static void main( String[] args ) {
		Button btn = new Button();
		btn.onClick( new ICallback(){ 
			public void onEvent( int i ) {
				System.out.println( "onEvent XX " + i );
			}
		} );
	}
}
출력결과
onClick ...
onEvent XX100

호출당한 쪽( onClick )에서 호출한 쪽이 오버라이딩한 함수( onEvent )를 호출한다고 해서 이런 기법을 Callback 이라고 한다.

0개의 댓글