📚 Study/Java

JAVA :: Test117~Test120_인터페이스(Interface)

bono-hye 2023. 9. 14. 23:32

○ 인터페이스(Interface)

  • 완전히 미완성된 채로 남겨져 인터페이스 안에 존재하는 그 어떤 메소드도 몸체(정의부)가 없기 때문에 사실상 실행 부분이 존재하지 않는다.
  • 클래스를 위한 템플릿으로써의 기능을 수행하는 추상 클래스의 한 종류이다.
  • 인터페이스는 클래스와 달리 다중 상속이 가능하며 인터페이스 자체도 상속된다.
  • 기존의 C++ 언어 등에서 지원되는 다중 상속이 사용되는 과정에서 많은 문제점을 노출시켰기 때문에 자바에서는 다중 상속의 개념을 인터페이스라는 개념으로 변형하여 인터페이스를 통해 다중 상속을 구현하는 방법을 지원한다.
  • 인터페이스는 상수와 추상 메소드만 가질 수 있으며 인터페이스 안의 메소드들은 접근제어지시자를 명시하지 않아도 『public』으로 설정되어 클래스에서 구현(implements)함으로써 바로 접근이 이루어질 수 있다.

 

○ 특징

  • 추상 클래스의 일종으로 선언만 있고 정의가 없다.
  • final 변수는 가질 수 있다. (상수의 개념)
  • 인터페이스는 『public static final』 상수만 만들 수 있다.
  • 인터페이스를 구현하기 위해서는 『extends』 대신에 『implements』를 이용한다.
  • 하나 이상의 인터페이스를 implements 할 수 있다.
  • 인터페이스를 implememts 한 클래스는 인터페이스의 모든 메소드를  Overriding 해야 한다.
  • 인터페이스가 다른 인터페이스를 상속 받을 수 있으며 이 때, 『extends』 키워드를 사용한다.
  • 클래스와 달리 인터페이스는 다중 상속이 가능하다.

 

○ 『extends』 vs. 『implements』

  • 클래스 extends 클래스
  • 클래스 extends 추상 클래스
  • 인터페이스 extends 인터페이스
  • 인터페이스 extends 인터페이스, 인터페이스, ...
  • 추상클래스 implements 인터페이스
  • 추상클래스 implements 인터페이스, 인터페이스, ...
  • 클래스 implements 인터페이스
  • 클래스 implements 인터페이스, 인터페이스, ...

 

▼ Test117

// 인터페이스
interface Demo
{
	public static final double PI = 3.141592;

	// 인터페이스의 멤버 변수는
	// 『static final』을 별도로 명시하지 않아도
	// 자동으로 『static final』인 상태~!!!  그래서 아래 public int a = 10;가 가능한 구문
	public int a = 10;
	
	// 인터페이스의 메소드는 선언만 가능(정의 불가)
	// 자동으로 『abstract』인 상태
	//public abstract void print();	→ 어차피 인터페이스는 추상 클래스만 가지니까 abstract 안써줘도 된다.
	public void print();
	/*
	{
		System.out.println("PI : " + PI);			// 인터페이스는 추상 메소드만 있어야 하니 이처럼 정의가 되면 안된다. 선언만 가능
	}
	*/
}

// 클래스
//class DemoImpl
//class DemoImpl extends Demo		//--(Ⅹ) implements 사용해야함
//class DemoImpl implements Demo
//			↓
// 추상 클래스 - 인터페이스를 구현하는 추상 클래스
//abstract class DemoImpl implements Demo
//			↓
// 클래스 - 인터페이스를 구현하는 클래스 (→ print() 메소드 재정의)
class DemoImpl implements Demo
{	
	@Override
	public void print()
	{
		System.out.println("인터페이스 메소드 재정의...");
	}

	public void write()
	{
		System.out.println("클래스에 정의된 메소드...");
	}
}


// main() 메소드를 포함하는 외부의 다른 클래스
public class Test117
{
	public static void main(String[] args)
	{
		//Demo ob = new Demo();		//-- 생성 불가~!!!
		//-- 인터페이스는 인스턴스를 생성할 수 없음~!!!

		//DemoImpl ob = new DempImpl();		//-- 임플리먼트 한 뒤 추상메소드를 재정의하여 추상 메소드가 아니게 되었으니 가능
		//-- 인터페이스를 implements 만 한 상태에서는 불가
		//	 print() 메소드(→ 추상 메소드)를 재정의한 후
		//	 abstract 상태에서 벗어난 후 가능

		//DemoImpl obTemp = new DemoImpl();		┐
		//Demo ob = (Demo)obTemp;				├   생성 가능 구문
		//Demo ob = obTemp;						┘

		// ○ 업 캐스팅
		// 인터페이스 객체는 상위 객체
		Demo ob = new DemoImpl();
		ob.print();
		//--==>> 인터페이스 메소드 재정의...			ob는 인터페이스이지만 덮어쓴 print

		//ob.write();					//--Demo 위상으로 변경했는데 Demo에는 write()메소드가 없음
		//--==>> 에러 발생(컴파일 에러)
		
		// ○ 다운 캐스팅
		((DemoImpl)ob).write();
		//--==>> 클래스에 정의된 메소드...

		System.out.println(Demo.PI);		//-- static 이기 때문에... static(클래스 변수) → 변수가 선언된 클래스의 모든 인스턴스(객체)가 공유할 수 있는 변수
											//												  static이 선언된 멤버 변수는 객체들 간의 전역변수 처럼 사용될 수 있다



		//--==>> 3.141592

		System.out.println(Demo.a);
		//--==>> 10

		//Demo.a = 300;					//-- final 이기 때문에...
		//--==>> 에러 발생(컴파일 에러)
		
	}
}

▼ Test118

// 인터페이스
interface ADemo
{
	public void write();
}

// 인터페이스
interface BDemo
{
	public void print();
}

// ※ 인터페이스는 2개 이상을 구현(implements)할 수 있다.
//    → 클래스에서 다중 상속이 되지 않는 부분을 보완(보충)하는 개념

// 클래스
//class DemoImpl
//class DemoImpl extends ADemo, BDemo	//--(Ⅹ)
//class DemoImpl implements ADemo, BDemo
//		↓
// 추상 클래스 - 두 인터페이스를 구현하는 추상 클래스
//abstract class DemoImpl implements ADemo, BDemo
//		↓
// 클래스 - 두 인터페이스를 구현한 후 → 두 인터페이스의 모든 메소드를 Overriding → 일반 클래스
class DemoImpl implements ADemo,BDemo
{
	// JDK 1.5(5.0)에서는 인터페이스 메소드를
	// 오버라이딩(Overriding) 할 때
	// 『@Override』 어노테이션(annotation)을 사용할 수 없다.
	// JDK 1.6(6.0) 이후부터 적용 가능한 문법이다.
	// 단, 상속받은 클래스의 메소드를 오버라이딩(Overriding)할 때에는
	// JDK 1.5(5.0)에서도 어노테이션(annotation)사용이 가능하다.
	@Override
	public void write()
	{
		System.out.println("ADemo 인터페이스 메소드 write()...");
	}
	
	@Override
	public void print()
	{
		System.out.println("BDemo 인터페이스 메소드 print()...");
	}
}


// main() 메소드를 포함하는 외부의 다른 클래스
public class Test118
{
	public static void main(String[] args)
	{
		// ADemo ob1 = new ADemo();		//-- 인터페이스 → 인스턴스 생성 불가
		// BDemo ob2 = new BDemo();		//-- 인터페이스 → 인스턴스 생성 불가
		
		// ADemo, BDemo 인터페이스를 구현(implements)한
		// 클래스(→ DemoImpl) 기반의 인스턴스 생성
		DemoImpl ob1 = new DemoImpl();

		ob1.write();
		//--==>> ADemo 인터페이스 메소드 write()...

		ob1.print();
		//--==>> BDemo 인터페이스 메소드 print()...

		// 업 캐스팅
		ADemo ob2 = new DemoImpl();
		BDemo ob3 = new DemoImpl();

		//ob2.print();
		//--==>> 에러 발생(컴파일 에러)
		//ob3.write();
		//--==>> 에러 발생(컴파일 에러)

		ob2.write();
		//--==>> ADemo 인터페이스 메소드 write()...
		ob3.print();
		//--==>> BDemo 인터페이스 메소드 print()...


		((BDemo)ob2).print();
		//--==>> BDemo 인터페이스 메소드 print()...
		((ADemo)ob3).write();
		//--==> ADemo 인터페이스 메소드 write()...
		//-- ※ DemoImpl 클래스가 두 인터페이스(ADemo, BDemo)를 모두 구현했기 때문에
		//	    이와 같은 처리가 가능하다.
		//		만약, DemoImpl 클래스가 두 인터페이스들 중 한 인터페이스만 구현했다면
		//		이 구문은 런타임 에러가 발생하는 구문이 된다.

		// 다운 캐스팅
		((DemoImpl)ob3).write();
		//--==>> ADemo 인터페이스 메소드 write()...
	}
}

▼ Test119

// 인터페이스
interface Demo
{
	public void write();
	public void print();
}

// 클래스
//class DemopImpl
// 인터페이스를 구현하는 클래스
abstract class DemopImpl implements Demo
{
	@Override
	public void write()
	{
		System.out.println("write() 메소드 재정의...");
	}

	// public abstract void print();		-- 눈에 보이진 않지만 이렇게 되어 있을 것임.
}

// 클래스
//class DemoimplSub
//class DemoimplSub extends DemoImpl
// 추상 클래스를 상속받은 추상 클래스
//abstract class DemoimplSub extends DemoImpl
// 추상 클래스를 상속받은 클래스
class DemoImplSub extends DemoImpl
{
	/*
	@Override
	public void write()
	{
		System.out.println("write() 메소드 재정의...");
	}

	// public abstract void print();
	*/

	public void print()
	{
		System.out.println("print() 메소드 재정의...");
	}
}


// main() 메소드를 포함하는 외부의 다른 클래스
public class Test119
{
	public static void main(String[] args)
	{
		// Demo ob1 = new Demo();
		//-- 인스턴스 생성 불가 → 인터페이스

		// DempoImpl ob2 = new DemoImpl();
		//-- 인스턴스 생성 불가 → 추상 클래스

		DemoImplSub ob3 = new DemoImplSub();

		ob3.write();
		ob3.print();
		//--==>> write() 메소드 재정의...
		//		 print() 메소드 재정의...

	}
}

▼ Test120

// 인터페이스
interface ADemo
{
	public void write();
}

// 인터페이스
interface BDemo
{
	public void print();
}

// 인터페이스
//interface CDemo
// 두 인터페이스 (ADemo, BDemo)를 상속받은 인터페이스
interface CDemo extends ADemo, BDemo
{
	// public void write();		┐
	// publie void print();		┘이 둘도 포함 왜? 상속받았으니까


	public void test();
}

// 클래스
// class DemoImpl
//	↓
// 추상 클래스
// 두 인터페이스(ADemo, BDemo)를 상속받은 인터페이스를 구현한 추상 클래스
// abstract class DemoImpl implements CDemo
//	↓
// 클래스
// 두 인터페이스(ADemo, BDemo)를 상속받은 인터페이스를 구현한 후 모든 메소드를 재정의한 클래스
//abstract class DemoImpl implements CDemo
class DemoImpl implements CDemo
{
	@Override
	public void test()
	{
		System.out.println("test()...");
	}

	public void write()
	{
		System.out.println("write()...");
	}

	public void print()
	{
		System.out.println("print()...");
	}
}


public class Test120
{
	public static void main(String[] args)
	{
		// 두 개의 인터페이스(ADemo, BDemo)를 상속받은
		// 인터페이스(CDemo)를 구현하고
		// 해당 인터페이스(CDemo)의 메소드 뿐 아니라
		// 상속받은 인터페이스(ADemo, BDemo)의 모든 메소드를 재정의(Overriding)한
		// DemoImpl 클래스 기반의 인스턴스 생서
		DemoImpl ob = new DemoImpl();
		
		ob.test();
		ob.write();
		ob.print();
	}
}

// 실행 결과

/*
test()...
write()...
print()...
계속하려면 아무 키나 누르십시오 . . .
*/