티스토리 뷰

728x90
반응형

이번에는 인터페이스에 대해 다뤄보려 한다. 인터페이스의 특징은 다음과 같다.


[ 인터페이스 ]

 

- 생성자가 없다. (단독 객체 생성 불가)

- 완성된(구현된) 메소드는 가질 수 없다. (추상 메소드만 가질 수 있다.)

- 필드는 static final 상수만 가질 수 있다.

- data type의 역할을 할 수 있다.

- interface type의 참조값이 필요하면 구현(implements) 클래스를 만들어 객체를 생성해야 한다.

- 클래스 상속은 단일 상속이지만, 인터페이스는 다중 구현이 가능하다.


인터페이스일종의 추상 클래스로 추상 메소드를 갖지만 추상화 정도가 더 높아, 추상 클래스와 달리 일반 메소드, 멤버 변수를 구성원으로 가질 수없다. 

따라서, 미완성 설계도라 불리는 추상 클래스와 달리 기본 설계도라 불린다.

 

인터페이스의 명은 대부분 "~able"로 끝나는 것들이 많다.

인터페이스와 추상 메소드는 둘 다 자식 클래스에게 추상 메소드를 오버라이드 하도록 강제한다.  

 

서로 조상이 다르지만 똑같은 행위를 할 수 있을 때(ex, 사람과 개구리는 각각 조상이 포유류, 양서류로 다르지만 음식을 섭취할 수 있으므로 (Eatable) 이럴 경우, Eatable을 인터페이스로 구현) 인터페이스로 구현한다. 

 

 

예제를 통해 자세히 살펴보자.


RemoteControl.java

package test.mypac;
public interface RemoteControl {
	//필드는 static final 필드만 가능(static 상수만 가질 수 있다.)
	public static final String company = "LG";
	
	//메소드는 추상 메소드만 가능
	public abstract void up();
	public void down(); //추상 메소드만 생성 가능하므로 abstract 생략하는게 일반적이다.		

}

위의 RemoteControl 인터페이스는 up(), down()의 추상 메소드를 가지고 static final의 compamy 필드를 가진다. 

추상 메소드들을 구현한 클래스를 생성해보자.

 

MyRemoteControl.java

package test.mypac;

public class MyRemoteControl implements RemoteControl{

	@Override
	public void up() {
		System.out.println("채널을 올려요!");
		
	}

	@Override
	public void down() {
		System.out.println("채널을 내려요!");
		
		
	}

}

"인터페이스는 단독 객체 생성이 불가하다"

 

RemoteControl 인터페이스를 상속한 MyRemoteControl 클래스를 만들어 객체 생성하기 

MainClass01.java 

package test.main;

import test.mypac.MyRemoteControl;
import test.mypac.RemoteControl;

public class MainClass01 {
	public static void main(String[] args) {
		//interface도 데이터 type 역할을 할 수 있다.
		RemoteControl r1 = null;
		//interface 단독으로 객체 생성 불가 
		//RemoteControl r2 = new RemoteControl();
		
		//인터페이스의 필드의 내용 확인해보기
		System.out.println(RemoteControl.company);
		
		RemoteControl r2 = new MyRemoteControl();
		r2.up();
		r2.down();
	}

}

RemoteControl 객체 생성이 불가함을 MainClass01에서 확인할 수 있다. 

따라서, RemoteControl을 구현한 MyRemoteControl 객체를 생성하여 RemoteControl type의 지역변수에 참조값을 담는다.


"inner class와 local inner class를 이용하여 메소드 호출하기"

 

다음은 MainClass02이다. local inner class와 inner class를 사용하여 useRemocon()메소드를 호출해보자.


MainClass02.java

package test.main;
import test.mypac.RemoteControl;

public class MainClass02 {
	public static void main(String[] args) {
		//inner 클래스를 이용해서 아래의 useRemoteControl() 메소드를 호출해 보세요.
		
		
		//local inner 클래스를 이용해서 아래의 useRemoteControl() 메소드를 호출해 보세요.
		
		
	}
	public static void useRemoteControl(RemoteControl r) {
		r.up();
		r.down();
	}

}

 


1. inner class 생성하기

package test.main;
import test.mypac.RemoteControl;

public class MainClass02 {
	static class YourRemoteControl implements RemoteControl{
	//inner class
		@Override
		public void up() {
			System.out.println("채널을 올려요");
			
			
		}

		@Override
		public void down() {
			System.out.println("채널을 내려요");
			
		}
		
	}
	public static void main(String[] args) {
		//inner 클래스를 이용해서 아래의 useRemoteControl() 메소드를 호출해 보세요.
		useRemoteControl(new YourRemoteControl());
		
		
		
	}
	public static void useRemoteControl(RemoteControl r) {
		r.up();
		r.down();
	}

}

RemoteControl을 구현한 YourRemoteControl 클래스를 생성하였다. 

main() 메소드에서 호출할건데, main 메소드는 static 영역에 올라가 있으므로, main에서 클래스를 사용하려면 해당 클래스도 static에 올라있어야 한다. 

따라서 YourRemoteControl을 static으로 지정한다. 

useRemoteControl 메소드는 RemoteControl을 전달받기 때문에, YourRemoteControl 객체를 넘겨준다.


2. local inner class 생성하기

package test.main;
import test.mypac.RemoteControl;

public class MainClass02 {
	
	public static void main(String[] args) {
		
		//local inner 클래스를 이용해서 아래의 useRemoteControl() 메소드를 호출해 보세요.
		class OurRemoteControl implements RemoteControl{

			@Override
			public void up() {
				System.out.println("아무거나 올려요!");
			}

			@Override
			public void down() {
				System.out.println("아무거나 내려요!");
				
			}
			
		}
		useRemoteControl(new OurRemoteControl());
		
	}
	public static void useRemoteControl(RemoteControl r) {
		r.up();
		r.down();
	}

}

local inner class는 메소드 안의 클래스이므로 메인 메소드 안에 만들었다.

마찬가지로, RemoteControl을 구현한 OurRemoteControl 클래스를 메인 메소드 안에 생성한다. 

useRemoteControl메소드는 RemoteControl 객체를 전달받으므로 OurRemoteControl 객체를 생성하여 전달한다.


전체 코드

MainClass02.java

package test.main;
import test.mypac.Remocon;

public class MainClass02 {
	static class YourRemocon implements Remocon{

		@Override
		public void up() {
			System.out.println("채널을 올려요");
			
			
		}

		@Override
		public void down() {
			System.out.println("채널을 내려요");
			
		}
		
	}
	public static void main(String[] args) {
		//inner 클래스를 이용해서 아래의 useRemocon() 메소드를 호출해 보세요.
		useRemocon(new YourRemocon());
		
		//local inner 클래스를 이용해서 아래의 useRemocon() 메소드를 호출해 보세요.
		class OurRemocon implements Remocon{

			@Override
			public void up() {
				System.out.println("아무거나 올려요!");
			}

			@Override
			public void down() {
				System.out.println("아무거나 내려요!");
				
			}
			
		}
		useRemocon(new OurRemocon());
		
	}
	public static void useRemocon(Remocon r) {
		r.up();
		r.down();
	}

}

"@FunctionalInterface 함수형 인터페이스"

 

다음은 Drill 인터페이스이다.

 

Drill.java

package test.mypac;
//@xxx는 annotation(장식자)라고 부른다.
//@FunctionalInterface는 메소드를 하나만 정의하도록 강제한다.
@FunctionalInterface
public interface Drill {
	public void hole();
	//public void test(); //FunctionalInterFace라고 명시해서 메소드를 더 추가하면 에러 뜸.
}

 

@FunctionalInterface 는 메소드를 하나만 정의하도록 강제한다. 

 


"Interface는 다중 구현이 가능하다."

 

Drill과 RemoteControl을 구현한 MultiPlayer 클래스를 만들어보자.

 

MultiPlayer.java - Drill과 RemoteControl 인터페이스를 다중 구현한 MultiPlayer 클래스

package test.mypac;

public class MultiPlayer implements RemoteControl, Drill{

	@Override
	public void hole() {
		System.out.println("책상에 구멍을 뚫어요");
	}

	@Override
	public void up() {
		System.out.println("온도를 올려요");
		
	}

	@Override
	public void down() {
		System.out.println("온도를 내려요");
		
	}

}

MultiPlayer 객체를 생성하고, MultiPlayer객체의 참조값을 담을 수 있는 데이터 타입들에 대해 알아보자.

 

MainClass03.java - MultiPlayer 객체의 참조값이 담길 수 있는 데이터 타입들 

package test.main;

import java.util.ArrayList;

import test.mypac.Drill;
import test.mypac.MultiPlayer;
import test.mypac.RemoteControl;

public class MainClass03 {
	public static void main(String[] args) {
		/*
		 * RemoteControl 인터페이스와 Drill 인터페이스를 implements 한 MultiPlayer 클래스로
		 * 생성한 객체는 4개의 type을 가진다.
		 */
		Object mp1 = new MultiPlayer();
		RemoteControl mp2 = new MultiPlayer();
		Drill mp3 = new MultiPlayer();
		MultiPlayer mp4 = new MultiPlayer();
		
		ArrayList list = null;
		
	}

}

 

Object와 MultiPlayer 타입은 필수로 갖고, 다중 구현 했으므로, RemoteControl 타입과 Drill 타입을 갖는다.


"익명의 클래스로 useDrill() 메소드 호출하기"

 

MainClass04.java

package test.main;
import test.mypac.Drill;

public class MainClass04 {
	public static void main(String[] args) {
		
	}
	
	public static void useDrill(Drill d) {
		d.hole();
	}

}

 


1. Drill을 구현한 익명의 클래스를 생성하여 Drill 타입의 지역변수에 넣고, 그 지역변수를 useDrill() 메소드에 전달하여 useDrill() 호출하기

package test.main;
import test.mypac.Drill;

public class MainClass04 {
	public static void main(String[] args) {
		Drill d1 = new Drill() {
			
			@Override
			public void hole() {
				System.out.println("벽에 구멍을 뚫어요");
			}
		};
		useDrill(d1);
	
	public static void useDrill(Drill d) {
		d.hole();
	}

}

 

1) new Drill() 뒤의 중괄호는 익명의 클래스의 생성자를 호출한다. 

2) 생성자에서 오버라이드로 추상 메소드를 구현한다. 

3) 생성한 객체의 참조값을 Drill 타입 지역변수 d1에 넣는다. 

4) useDrill에 d1을 전달하여 useDrill을 호출한다. 


2. Drill 타입 지역변수에 저장하지 않고, 생성한 객체를 바로 useDrill()에 전달하여 useDrill() 메소드 호출하기

package test.main;
import test.mypac.Drill;

public class MainClass04 {
	public static void main(String[] args) {
		
		useDrill(new Drill() {
			
			@Override
			public void hole() {
				System.out.println("핸드폰에 구멍을 뚫어요!");
				
			}
		});
	}
	
	public static void useDrill(Drill d) {
		d.hole();
	}

}

"함수형 인터페이스 람다식으로 표현하기"

람다식이란, 함수를 하나의 식(expression)으로 표현한 것이다. 

람다식으로 표현하면 메소드의 이름이 필요없기 때문에 익명 함수의 한 종류라고 할 수 있다고 한다. 

메소드의 이름이 없는 대신, 컴파일러가 문맥을 살펴 타입을 추론한다.

 

앞서 만든 Drill 인터페이스는 함수형 인터페이스였다. 

람다식은 다음과 같이 표현한다.

 

람다식 작성 방법

(매개변수,...) -> {실행문}

 

Drill 객체를 생성하고 Drill 타입 d1 지역 변수에 참조값을 담는 코드를 람다식으로 작성해보자.

Drill d1 = () -> {
	System.out.println("핸드폰에 구멍을 뚫어요!")
};

useDrill(d1);

 

이번에는 useDrill() 메소드에 매개변수로 객체를 바로 생성하여 전달하는 코드를 람다식을 이용하여 작성해보자.

useDrill( () -> {
	System.out.println("어디를 뚫지?");
    });

전체 코드

MainClass05.java

package test.main;
import test.mypac.Drill;

public class MainClass05 {
	public static void main(String[] args) {
		Drill d1 = () -> {
			System.out.println("핸드폰에 구멍을 뚫어요!");
		};
		
		//인터페이스에 메소드가 1개인 경우에만 가능함. 메소드가 하나이므로 메소드명 표기할 필요 업음.
		useDrill(d1);
		
		
		
		useDrill( ()->{
			System.out.println("어디를 뚫지?");
			});
		}
		
	
	
	public static void useDrill(Drill d) {
		d.hole();
	}

}

 

람다식은 잘 사용하면 불필요한 코드를 줄이고 가독성을 높이는 데 도움을 줄 수 있다.


오늘은 이렇게 자바의 인터페이스에 대해 다뤄보았다.

다음 게시물에서는 GenericClass에 대해 다뤄볼 예정이다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함