본문 바로가기
뒷북 정리 (국비 교육)/java

[Java] step11. Interface

by 규글 2022. 4. 19.

step11. Interface

/*
 *  [ interface ]
 *  
 *  - 생성자가 없다 (단독 객체 생성 불가)
 *  - 구현된 메소드는 가질수 없다 ( 추상 메소드만 가질수 있다 )
 *  - 필드는 static final 상수만 가질수 있다.
 *  - data type  의 역활을 할수 있다.
 *  - interface type 의 참조값이 필요하면 구현(implements) 클래스를 만들어서
 *    객체를 생성해야 한다.
 *  - 클래스 상속은 단일 상속이지만, 인터페이스는 다중 구현이 가능하다 
 */
public interface Remocon {
	// 필드 (static final 예약어 생략 가능)
	public static final String COMPANY="LG";
    
	// 메소드 (abstract 예약어 생략 가능)
	public abstract void up();
	public void down();
}

 Interface는 생성자가 존재하지 않기 때문에 단독 객체로 만들 수 없다. 게다가 field는 static final 예약어를 붙여야하고, method는 온전한 상태를 가질 수 없어서 abstract 예약어를 붙여야 한다. 즉, 이 interface 이름으로 된 type으로 무언가 참조하고 싶다면 implements(구현) 를 사용해서 class를 만들어 객체를 생성해야 한다.

 그렇다면 이 interface는 왜 필요한 것일까? 대체 왜 만드는 것일까?

 

/*
 *  인터페이스는 implements (구현) 해야 한다.
 *  구현 클래스에서는 인터페이스에 정의된 추상 메소드를 반드시 오버라이드 해야한다.
 */
public class MyRemocon implements Remocon{

	@Override
	public void up() {
		System.out.println("무언가를 올려요");
	}

	@Override
	public void down() {
		System.out.println("무언가를 내려요");
	}
}

 class의 경우 하나만 상속받을 수 있다. 하지만 interface는 다중으로 구현이 가능하다고 한다. 다중으로 구현하려고 할 때는 implments 뒤에 쉼표 ',' 로 연결한다. 이렇게 implements 한 class는 미구현 abstract method가 있기 때문에 override 해줘야 한다. 이 interface는이렇게 method의 모양을 미리 정해주는 역할을 한다. 미리 정해놓고 표준화된 프로그래밍을 가능하게 한다는 것이다. Abstract class에서도 미구현인 abstract method가 있었지만 이때는 이미 구현된 실체로 있다는 것과는 달리 interface의 abstract method는 모양만 강제할 뿐 실체는 없다고 했다.

 

public class MultiPlayer implements Remocon, Drill{
	@Override
	public void hole() {
		System.out.println("책상에 구멍을 뚤어요");
	}
	@Override
	public void up() {
		System.out.println("온도를 올려요");
	}
	@Override
	public void down() {
		System.out.println("온도를 내려요");
	}
}

 이렇게 다중으로 implements 할 수 있다. 이 interface에 대한 예제는 먼 미래(약 두 달 뒤의 수업)에 하게 될 것이라 했다.

 

- 01

	public static void main(String[] args) {
		// 인터페이스 type (부모 type)  
		Remocon r1=new MyRemocon();
		// 원래 class type 
		MyRemocon r2=new MyRemocon();
		
		useRemocon(r1);
		useRemocon(r2);
	}
	
	public static void useRemocon(Remocon r) {
		r.up();
		r.down();
	}

 기본적으로 객체를 생성해서 사용하는 예시이다. 그 type은 부모 type인 interface type과 class type이 모두 가능하다.

 

- 02

	// 필드에 Remocon type
	static Remocon r3=new Remocon() {
		@Override
		public void up() {
			System.out.println("물건을 올려요");
		}
		@Override
		public void down() {
			System.out.println("물건을 내려요");
		}
	};
	
	public static void main(String[] args) {
		/*
		 *  Remocon 인터페이스를 구현한 익명의 local inner 클래스의 생성자를 호출해서
		 *  참조값을 얻어내서 Remocon type  의 지역변수 r1 에 대입하기 
		 */
		Remocon r1=new Remocon() {
			@Override
			public void up() {
				System.out.println("볼륨을 올려요");
			}
			@Override
			public void down() {
				System.out.println("볼륨을 내려요");
			}
		};
		
		useRemocon(r1);
		useRemocon(new Remocon() {
			@Override
			public void up() {
				System.out.println("체널을 올려요");
			}
			@Override
			public void down() {
				System.out.println("체널을 내려요");
			}
		});
		
		useRemocon(r3);
	}
	
	public static void useRemocon(Remocon r) {
		r.up();
		r.down();
	}

 Anonymous inner class 형태로 한 예시이다.

 

- 03

	public static void main(String[] args) {
		// MultiPlayer 객체는 3가지 type  이 모두 될수 있기 때문에
		MultiPlayer mp=new MultiPlayer();
		// 아래의 3개 메소드를 호출하면서 인자로 전달할수 있다. 
		useRemocon(mp);
		useDrill(mp);
		useMultiPlayer(mp);
		
		// 만일 Remocon type으로 받으면
		Remocon r=mp;
		// r. 하면 down, up은 있지만 holl은 없음
		Drill p = mp;
		// p. 하면 down, up이 없고 holl이 있음
		// mp.
		// mp. 하면 down, up, holl이 모두 있음
	}
	
	public static void useRemocon(Remocon r) {
		r.up();
		r.down();
	}
	
	public static void useDrill(Drill d) {
		d.hole();
	}
	
	public static void useMultiPlayer(MultiPlayer mp){
		mp.up();
		mp.down();
		mp.hole();
	}

 implements 한 interface type과 class의 type으로 모두 받을 수 있다. 사용할 수 있는 method는 해당 type에 따라 달라지는 것을 볼 수 있는 예시이다.

 

- 04

	public static void main(String[] args) {
		useDrill(new Drill() {
			@Override
			public void hole() {
				System.out.println("바닥에 구멍을 뚤어요");
			}
		});
		
		Drill d1=()->{
			System.out.println("벽에 20mm 의 구멍내기");
		};
		
		useDrill(d1);
        
		useDrill(()->{
			System.out.println("핸드폰에 1mm 구멍내기");
		});
		
	}
	
	public static void useDrill(Drill d) {
		d.hole();
	}

 

 예시 상단의 내용은 anonymous inner class를 이용한 예시이다. 그런데 그 바로 아래 이상한 모양이 있다. Class 이름으로 된 data type에 소괄호 ( )와 화살표 ->, 그리고 중괄호 { } 로 이루어진 이상한 친구가 있다. 이 ( )->{ } 꼴을 lambda function (람다 함수) 라고 한다.

 

		Drill d1=()->{
			System.out.println("벽에 20mm 의 구멍내기");
		};

 언젠가부터 java가 javascript를 동경했다고 말했다. 이미지와 마치 닮지 않았는가? 이미지 우측의 두 번째는 arrow function이라고 하는데, 하나 빼고 비슷하다고 했다. 하지만 이 방식은 어디까지나 줄임 표현, 즉 약식에 불과하긴 하다. 게다가 제약도 분명히 있을 것이다. 마찬가지로 이 약식인 lambda function을 사용할 수 있는 경우는 굉장히 제한되는데, 그것은 바로 해당 type의 interface가 구현할 method가 하나인 경우에 가능하다.

 

- 05

@FunctionalInterface //  메소드를 하나만 정의 하도록 강제 하는 기능
public interface Joinner {
	//전달 되는 두 문자열을 연결해서 리턴하는 메소드 
	public String join(String first, String second);
}

 @FunctionalInterface annotation은 method를 하나만 만들도록 강제하는 기능을 한다. 만약 해당 interface에 method를 하나 더 만들고자 하면 오류 표시를 발생시킨다.

 

		Joinner j1=new Joinner() {
			@Override
			public String join(String first, String second) {
				return first+second;
			}
		};
		String result1=j1.join("김", "구라");
		
		Joinner j2=(first, second)->{ 
			return first+second; 
		};
		String result2=j2.join("해", "골");
		
		Joinner j3=(first, second)->first+second;
		String result3=j3.join("원", "숭이");

 이렇게 강제로 method를 하나만 만들게 된다면 이전 예시였던 lambda function을 사용할 수 있다. 예시의 j1을 lambda function을 이용해서 풀어서 쓴다면 j2처럼 쓸 수 있다. 그런데 여기에서 재미있는 사실은 이 lambda function 안에서 수행하는 줄이 한 줄이면 j3처럼 중괄호 { } 조차 필요하지 않고, 전달하는 인자에 대한 type 조차 쓰지 않아도 된다. 게다가 return 또한 없어도 된다. 이는 method가 하나이기 때문에 쓸 수 있는 약식 표현이다.

 

- 06

@FunctionalInterface
public interface Calculator {
	// 인자로 전달되는 두 실수값을 연산해서 결과를 리턴해주는 메소드 
	public double exec(double a, double b);
}
		// Calculator 인터페이스를 람다식으로 구현하기 
		Calculator add=(a, b)->a+b;
		Calculator sub=(a, b)->a-b;
		Calculator multi=(a, b)->a*b;
		Calculator divide=(a, b)->a/b;
		
		double result1=add.exec(10, 20);
		double result2=sub.exec(10, 20);
		double resutl3=multi.exec(10, 20);
		double result4=divide.exec(10, 20);
		
		useCalculator((a, b)->a+b);
		useCalculator((a, b)->a-b);
		useCalculator((a, b)->a*b);
		useCalculator((a, b)->a/b);
	}
	
	public static void useCalculator(Calculator c) {
		System.out.println(c.exec(10, 20));
	}

 헷갈릴 수 있는데, 참고로만 보면 되겠다.

 

'뒷북 정리 (국비 교육) > java' 카테고리의 다른 글

[Java] step13. Util Class  (0) 2022.04.22
[Java] step12. Generic Class  (0) 2022.04.22
[Java] step10. Abstract Class  (0) 2022.04.19
[Java] step09. Extends  (0) 2022.04.19
[Java] step08. Array  (0) 2022.04.17

댓글