인터페이스는 추상클래스의 일종이며, 추상 클래스보다 더 추상화되어 있다.
추상 클래스는 일반클래스와 똑같이 인스턴스 변수, 클래스 변수, 생섲아, 메소드를 가질 수 있다. 현재 이책은 자바 버전 7까지에서는 인터페이스는 상수와 추상 메소드만을 가질수 있었다. 자바 버번 8이후로는 몇가지 내용이 추가 되었으며, 우선 자바 7까지의 인터페이스에 대해 알아보자 이후 8이후에 추가된 내용에 대해서 알아보자.
자바 7 이전 버전까지는 인터페이스가 다음과 같이 상수와 추상 메소드만 가질수 있다
interface 인터페이스명{
public static fianl 자료형 상수명 = 값;
public abstract 반환자료형 메소드명(매개변수 리스트);
}
인터페이스는 interface 키워드로 정의한다. 상수는 public static final로 정의되어야 하는데, 만약에 public static final 이 생략되어 있어도 인터페이스 내의 상수는 public static final로 간주합니다. 따라서 아래 정의는 모두 같다
인터페이스 내의 메소드는 public abstract로 정의해야 합니다. 하지만 메소드 앞에 public abstract를 생략해도 public abstract로 간주합니다. 따라서 메소드의 내용이 있으면 안 됩니다. 인터페이스 내에서 아래 정의는 모두 같다.
** 자바 버전 7 까지는 인터페이스에 public static fianl 데이터와 public abstract 메소드만 넣을수 있었다.
->
package interfacetest;
public interface Data {
int count = 100; //public static fianl 이 생략 되어있다
void print(); // public abstract가 생략 되어있다.
}
위의 Data 인터페이스는 객체를 생성할 수 없습니다. 따라서 이렇게 인터페이스를 만들고 나면 count는 사용할 수 있지만 메소드 print()는 바로 사용할 수가 없다. 메소드 print()를 사용하려면 data 인터페이스의 자식 클래스를 만들어서 print 메소들르 오버라이딩한 후에 사용해야 합니다.
package interfacetest;
public class Main {
public static void main(String[] args){
// Data x = new Data(); //안됨 ->Cannot instantiate the type Data
System.out.println("count: " + Data.count)
}
}
자바 버전 8 에서는 인터페이스에 default 메소드를 추가할 수 있게 되었습니다. 여기에서 default 접근 제어에서 사용한 default 제어와는 완전히 다르며, 자바버전 7까지는 default라는 키워드는 없었는데 버전 8에서 default 키워드가 생겼다. 그리고 자바 버전 8에서는 static 메소드도 추가 할수 있으며
9 에서는 인터페이스가 private 메소드를 가질 수 있게 되었다.
앞 절에서 보았듯이 인터페이스 내에 변수를 정의할 수 있다. 하지만 public static final 변수 이기때문에 결국 상수이며, 즉 한번 만들면 바꿀수 없는 값이다. 프로그램이 커지고 여러 클래스에서 같은 값을 공유해야 하는 경우에 유용하게 사용할 수 있다.
ex=>필요한 상수만을 모아서 인터페이스를 구현하고 다른 클래스에서 이용하는 예제
package interfacetest;
public interface Limit {
int MAX = 100;
int MIN = 0 ;
}
package interfacetest;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scin = new Scanner(System.in);
System.out.print("Enter number between 0 and 100 :");
int num = scin.nextInt();
if(num<Limit.MIN || num > Limit.MAX)
System.out.println("out of range");
}
}
인터페이스 내의 메소드는 추상 메소드이기 때문에 반드시 자식 클래스가 상속받아서 오버라이딩해야 합니다. 클래스가 인터페이스를 상속받을 때에는 implements 키워드를 사용합니다. 그리고 인터페이스가 인터페이스를 상속받을 때도 있는데 이때는 extends 키워드를 사용한다.
클래스가 인터페이스를 상속받을 때는 implements 키워드를 사용합니다. 인터페이스 안에 정의된 메소드는 모두 추상 메소드이기 때문에 인터페이스를 상속받는 클래스는 인터페이스에 있는 추상 메소드를 모두 오버라이딩해야함, 만약에 인터페이스에 정의된 메소드 중에서 하나라도 하나라도 오버라이딩을 하지 않았다면 그 클래스는 추상 클래스가 되어야한다.
package interfacetest.test2;
public interface Data {
int count = 100;
void print();
}
package interfacetest.test2;
public class ChildData implements Data {
public void print(){//인터페이스의 메소드들은 public 제어를 한다.
System.out.println("i am child print");
}
}
package interfacetest.test2;
public class Main {
public static void main(String[] args){
Data x = new ChildData();
x.print();
System.out.println("count :" + Data.count);
}
}
인터페이스가 다른 인터페이스를 상속할 수 있고 이 경우에는 extends 키워드를 사용합니다. 다음 예에서는 Things 인터페이스를 Book 인터페이스가 상속받고, 그 아래에서 ChildrenBook 클래스가 최종적으로 상속 받는다.
package interfacetest.test3;
interface Things{
void printInfo();
}
interface Book extends Things{ //인터페이스 book은 인터페이스 things를 상속받습니다
void showTitle();
}
class ChildrenBook implements Book{ //클래스 childrenBook은 인터페이스 Book을 상속받습니다
private String title;
private String author;
ChildrenBook(){}//디폴트
ChildrenBook(String title, String author){// 메소드
this.title = title;
this.author = author;
}
public void printInfo(){// 반드시 오버라이딩 해야함
System.out.println(" ** Info for books **");
}
public void showTitle(){
System.out.println("title : " + title); //변수 값
System.out.println("author : " + author);
}
}
public class Main {
public static void main(String[] args){
ChildrenBook b = new ChildrenBook("Little Mermaid" ,"andersen");
b.printInfo();
b.showTitle();
}
}
인터페이스의 중요한 특징 중에 하나가 다중 상속을 제공한다는 것. 다일 상속은 부모가 하나뿐인 것을 말하고, 다중 상속은 부모가 여럿인 것. 클래스는 단일 상속만 가능하며. 즉, 클래스는 부모 클래스를 한 개만 가질 수 있다. 그런데 인터페이스는 다중 상속을 가능하게 하기 때문에 클래스는 여러 개의 인터페이스를 부모로 가질수 있다. 인터페이스 역시 부모 인터페이스를 여러 개 가질 수 있다.
package abstractest.test;
abstract class Person{
abstract void print();
}
class Teacher extends Person{
void print(){
System.out.println("teacher");
}
}
class Student extends Person{
void print(){
System.out.println("student");
}
}
public class Intertest1 {
public static void main(String[] args){
Person t = new Teacher();
Person s = new Student();
t.print();
s.print();
}
}
package abstractest.test;
interface Person{
void print();// public abstract 생략
}
class Teacher implements Person{
public void print(){
System.out.println("teacher");
}
}
class Student implements Person{
public void print(){
System.out.println("student");
}
}
public class intertest2 {
public static void main(String[] args){
Person t = new Teacher();
Person s = new Student();
t.print();
s.print();
}
}
추상화와 인터페이스를 이용한 이 둘의 코드는 같은 결과를 나타낸다.
추상클래스는 인터페이스로 구현할수 있다
package abstractest.test;
interface A{
void test1();
void test2();
}
interface B{
void test3();
void test4();
}
class All implements A,B{
//상속 받은 인터페이스를 오버라이딩할때 반드시 퍼블릭 전환
public void test1(){
System.out.println("I am test1");
}
public void test2(){
System.out.println("I am test2");
}
public void test3(){
System.out.println("I am test3");
}
public void test4(){
System.out.println("I am test4");
}
}
public class intertest3 {
public static void main(String[] args){
All x = new All();
x.test1();
x.test2();
x.test3();
x.test4();
}
}
자바 버전 8에서 인터페이스에 변화가 생겼다. 원래는 public static final 변수와 추상 메소드만 넣을수 이었는데, 여기에 다음의 두 종류의 메소드가 추가된다.
자바 버전 9에서는 인터페이스에 하나의 메소드가 더 추가된다
새로 추가된 위의 세 메소드들에 대해서 하나씩 알아보자
interface A{
public static final data
public abstract 메소드
--------> java 7
default 메소드
static 메소드
------- 8
private 메소드
---- 9
디폴트 메소드는 메소드의 내용 즉, 구현이 있는 메소드 이며 이전까지는 추상 메소드만 가질 수 있기 때문에 인터페이스에 있는 모든 메소드가 내용이 없어야 됬엇다. 디폴트 메소드에서는 이규칙이 깨진것이고 디폴트 메소드를 추가하면 좋은점은, 만약에 기존의 인터페이스에 새로운 메소드를 추가하면 그 인터페이스를 상속 받는 모든 하위 클래스에 새로 추가한 메소드를 오버라이딩해아 한다. 그런데 인터페이스에 디폴트 메소드를 추가하면 디폴트 메소드는 구현되어 있기 때문에 하위 클래스에서 오버라이딩하지 않아도 된다. 즉 인터페이스에 추가된 디폴트 메소드는 하위 클래스를 변경하지 않도록 한다.
interface A{
default void show(){
....
}
}//디폴트 메소드는 {}내용이 있는 메소드이며, 추상메소드가 아니라
// 자식 클래스에서 오버라이딩할 필요가 없다.
package interfacedefault;
interface WithDefault{
int COUNT = 100;
abstract void test();
default void show(){
System.out.println("I am show");
}
}
class Test implements WithDefault{
public void test(){
System.out.println("i am test");
}
}
public class Code1 {
public static void main(String[] args){
Test t = new Test();
t.test();
t.show();
}
}
디폴트 메소드 내에서 같은 인터페이스에 있는 추상 메소드를 호출할 수도 있습니다. 이 경우에 추상 메소드는 상속받는 클래스에서 어떻게 구현했는지에 따라 다르다.ㅊ
package interfacedefault;
interface WithDefault2{
int get();
default int getNext(){
int value = get() + 10;
return value;
}
}
class Test2 implements WithDefault2{
public int get(){
return 100;
}
}
class Test3 implements WithDefault2{
public int get(){
return 500;
}
}
public class Code2 {
public static void main(String[] args){
Test2 x = new Test2();
System.out.println("x.getNext(): "+ x.getNext());
Test3 y = new Test3();
System.out.println("y.getNext() : " + y.getNext());
}
}
//Wx.getNext() :110
//y,getNext() : 510
interface A{
void get();
default void getNext(){
get();
}
}
프라이빗 메소드는 자바 버전 9에서 처음 소개. 인터페이스 내에 정의된 프라이빗 메소드는 같은 인터페이스 내에 있는 디폴트 메소드나 다른 프라이빗 메소드에 의해서만 호출 가능합니다.
프라이빗 메소드는 의미대로 메소드가 선언된 곳에서만 사용할수 있는것 이며 프라이빗 메소드의 장점은 같은 인터페이스에 있는 디폴트 메소들끼리 코들르 쉽게 공유할 수 있다는 데 있습니다.
다시 디폴트 메소드나 다른 프라이빗 메소드에 의해서 호출가능
package interfacedefault;
interface WithPrivate{
default int getData(){
print();
return get() + 100;
}
private void print(){
System.out.println(get());
}
private int get(){
return 100;
}
}
class Test4 implements WithPrivate{
void test(){
int x = getData();
System.out.println("x :" + x);
}
}
public class Code4{
public static void main(String[] args){
Test4 t = new Test4();
t.test();
}
}
새로 추가된 세 종류의 메소드 중에서 프라이빗 메소드의 사용도가 가장 낮을 것이다. 하지만 인터페이스 내에서 공유되어야 하는 코드를 프라이빗으로 선언하고 인터페이스 내에서만 사용하려고 하는 경우에는 적합한 메소드이다. 필요할대 써보자