무작정 개발.Vlog

[JAVA+국비교육] 추상 클래스, 인터페이스, 형변환(upcast, downcast)

by 무작정 개발
반응형
2022.01.05(12일 차)

오늘은 추상 클래스와 인터페이스에 대해 배운다고 하셨다. 그리고 추상 클래스는 잘 안 쓰고, 인터페이스가 중요하다고 말씀하셨다. 점점 어려워지고 따라가기 벅차지만 꾸준히 복습하고 최대한 따라가 보자!

오늘의 수업 내용


추상 클래스 & 인터페이스(Interface)

 

수업시간에 인터페이스와 추상 클래스에 대해 배웠지만 둘의 차이점을 자세히 알기 위해 여러 가지 자료를 찾아보면서

따로 정리해 보았습니다. 

 

 

인터페이스(Interface)

  • 인터페이스는 interface 키워드를 사용하여 클래스를 선언하듯이 선언한다.
  • 인터페이스는 implements 키워드로 사용하여 상속받을 수 있다.
  • 인터페이스는 추상 클래스의 일종으로 선언만 하고, 정의(내용)가 없다.
  • 변수는 final 변수만 선언 가능
  • 인터페이스를 구현했으면 인터페이스의 모든 메서드를 재정의(오버라이드) 해야 한다.
  • 인터페이스는 다음 5종류의 멤버로 구성되며, 멤버 변수를 만들 수 없다.
  • 상수, 추상 메서드, default 메서드, private 메서드, static 메서드
  • java 8부터 일반(default) 메서드 사용이 가능
  • java 9부터 private, static 메서드 사용이 가능 ( 난 java 8 기준으로 학습하기에 참고만 하기)

인터페이스(Interface) 기본 예제

interface PhoneInterface {
    public static final int TIMEOUT = 10000;    // 상수
    public abstract void sendCall();            // 추상 메소드
    public abstract void receiveCall();         // 추상 메소드
    public default void printLogo(){            // default 메소드
        System.out.println("** Phone **");
    }
}

class SamsungPhone implements PhoneInterface{
    // 인터페이스의 모든 추상메소드 구현
    @Override
    public void sendCall(){
        System.out.println("전화를 겁니다.");
    }

    @Override
    public void receiveCall(){
        System.out.println("전화를 받습니다.");
    }

    // 메소드 추가 작성
    public void flash(){
        System.out.println("손전등을 켭니다.");
    }
}
  • 인터페이스는 인스턴스 화가 불가능하다.
  • 인터페이스는 다중 상속이 가능하다.

추상 클래스(abstract class)

  • 추상 클래스는 클래스 앞에 abstract라는 키워드를 추가하여 선언한다.
  • 하나 이상의 추상 메서드가 포함된 클래스를 추상 클래스라고 한다.
  • 추상 클래스는 인스턴스 화가 불가능하며, 이를 제대로 사용하기 위해서는 상속을 이용해야 한다.                          서브 클래스에서 슈퍼 클래스의 모든 추상 메서드를 오버 라이딩하여 실행 가능한 코드로 구현하는 것을 의미
  • 추상 클래스는 다중 상속이 불가능하지만, 슈퍼 클래스의 멤버 변수와 일반 메서드를 상속받을 수 있으며, 이는 슈퍼 클래스의 기능을 이용 및 확장하는 다형성을 실현하는 결과를 낳는다.

추상 클래스(abstract class) 예제

abstract class Shape{
    public abstract void draw();
}

class Line extends Shape{
    @Override
    public void draw(){
        System.out.println("Line");
    }
}

class Circle extends Shape{
    @OVerride
    public void draw(){
        System.out.println("Circle");
    }
}

public static void main(String args[]){
    Shape wrong = new Shape();      // wrong
    Shape shape = new Line();       // ok
    Shape shape2 = new Circle();    // ok
}

추상 클래스(abstract class) VS 인터페이스(Interface)

  추상 클래스(abstract class) 인터페이스(interface
공통점 인스턴스화 불가능
객체의 추상화
인스턴스화 불가능
객체의 추상화
상속 단일 상속 다중 상속
목적 공통적인 기능을 하는 객체의 추상화에 사용.
슈퍼클래스의 멤버 변수와 메소드를 물려받고,
추상 메소드를 고유한 기능으로 확장
parent-child 관계를 보여주는 strong is-a 관계
서로 상속관계가 아닌 객체들의 추상화에 사용.
인터페이스의 추상 메소드를 항상 동일한 기능으로 구현.
object가 a property를 소유한다는 개념의 week is-a 관계
키워드 abstract, extends interface, implement

추상 클래스(abstract class)

  • 메모리의 낭비 없이 클래스를 미리 설계
  • 메서드를 재정의(override)해서 사용
  • 메서드명을 가져다 사용하면 된다. 강제성을 띄지만 메서드명을 만드는데 노력 x
  • 메서드명이 동일하여 통일성
  • 추상 클래스 안에는 무조건 추상 메서드가 1개 이상 존재해야 함
  • 일반 메서드만 있다면 에러는 안 나지만 추상 클래스로 만들 필요가 없음
package practice;
//추상 클래스 예제

abstract class ShapeClass { // 추상클래스도 부모클래스의 역할을 함
	
	abstract void draw(); // 틀을 만들어 놨으니 가져다가 재정의(Override)해서 사용해라. Interface(인터페이스)와 같음
						  // 무조건 return값은 void, 메서드명은 draw
	
}

class Circle1 extends ShapeClass {

	@Override
	void draw() {
		System.out.println("원을 그린다..");
		
	}
	
	
}

class Rect extends ShapeClass { // 추상 클래스의 메서드 중 unimplement method 존재 시 오류

	@Override
	void draw() {
		System.out.println("사각형을 그린다..");
		
	}
	
}

class Tri extends ShapeClass {

	@Override
	void draw() {
		System.out.println("삼각형을 그린다..");
	}
	
}


public class Test2 {

	public static void main(String[] args) {
		
		Circle1 ob1 = new Circle1();
		Rect ob2 = new Rect();
		Tri ob3 = new Tri();
		
		ob1.draw();
		ob2.draw();
		ob3.draw();

	}

}

Test 2 실행 결과
Test 2 실행 결과


상속 관계가 아닌 클래스의 경우 upcast, downcast가 불가능하다.

upcast

  • 상속관계에서 자식 클래스의 객체를 부모 클래스로 형 변환

downcast

  • 상속관계에서 부모 클래스의 객체를 자식 클래스로 형 변환
package com.day11;

class TestA {
	public void print() {
		System.out.println("A클래스...");
	}
}
//-----------------------------------------------------
class TestB {
	public void print() {
		System.out.println("B클래스...");
	}
}
//-----------------------------------------------------

public class Test1 {

	public static void main(String[] args) {
		
		TestA a = new TestA();
		TestB b;
		//데이터 타입이 달라서 안된다. 될려면 TestB를 TestA로 바꿔줘야함
		//b=a;
		//b=(TestB)a;

	}

}

 

부모 클래스와 자식 클래스의 메서드 호출 비교

자식 클래스를 upcast 시켜도 오버라이드 된 메서드를 호출 시 자신의 메서드를 호출한다.

package com.day11;

class SuperTest {
	
	public int a = 10, b = 20;
	
	public void write() {
		System.out.println("슈퍼클래스 write() 메소드...");
	}
	
	public int hap() {
		return a + b;
	}
}

class SubTest extends SuperTest {
	
	public int b = 100, c = 200;
	
	public void print() {
		System.out.println("서브클래스 print() 메소드...");
	}
	
	@Override
	public int hap() {
		return a + b + c;// a=10 , b =100, c=200
	}
	
}
public class Test2 {

	public static void main(String[] args) {
		
		SubTest ob1 = new SubTest();
		System.out.println(ob1.b); // 100
		
		SuperTest ob2 = ob1; // upcast - 작은값이 큰값으로 (암시적형변환)
		System.out.println(ob2.b); // 20
		
		System.out.println(ob2.hap()); // 310 -> 메서드는 무조건 자신꺼를 호출
		
		ob2.write();
		
		//ob2.print();  ob2는 부모 클래스이므로 사용 불가.. 4. 내꺼는 내꺼다
		((SubTest)ob2).print(); // downcast
		
	}

}

Test 2 실행 결과
Test 2 실행 결과


Interface(인터페이스)

  • 추상 클래스의 일종으로 선언만 하고, 정의(내용)가 없다.
  • 변수는 final 변수만 선언 가능하다.
  • 인터페이스를 구현하기 위해서는 implements를 사용한다.
  • 인터페이스를 구현했으면 인터페이스의 모든 메서드를 재정의(오버라이드) 해야 한다.
  • 인터페이스가 다른 인터페이스를 상속받을 수 있다. 그때는 extends를 사용한다.
  • 클래스는 단일 상속이지만 인터페이스는 다중 상속이 가능하다.

인터페이스를 사용하는 목적 : 강제성이 있음(인터페이스에 나온 대로 무조건 만들어야 함)

 

package com.day11;

interface Fruit { //인터페이스
	
	String Won = "원";  // 인터페이스 안의 변수 - public static final이 생략되어 있음
	
	int getPrice(); // public abstract 생략되어 있음
	

	public String getName(); // abstract 생략
	// 인터페이스 안에는 일반 메소드를 만들 수 없다.
}

// FruitImpl : Fruit라는 인터페이스를 구현한 클래스
class FruitImpl implements Fruit { //인터페이스는 extends 대신 implements로 상속받는다.
	

	@Override
	public int getPrice() {
		return 1000;
	}

	@Override
	public String getName() {
		return "사과";
	}
	
	public String getItems() {
		return "과일";
	}

	
}

public class Test3 {

	public static void main(String[] args) {
		
		
		//Fruit ob1 = new FruitImpl();을 하면 getItems이 오류뜸 why? 부모는 자식껄 쓸수 없기에
		FruitImpl ob1 = new FruitImpl();
		
		System.out.println(ob1.getItems());
		System.out.println(ob1.getName());
		System.out.println(ob1.getPrice() + Fruit.Won);
		
		Fruit ob2 = ob1; // upcast - 형변환해서 Fruit클래스에 넣음 -  자식을 부모에 넣음
		
		System.out.println(ob2.getName());
		//System.out.println(ob2.getItem()); 오류 - 부모는 자식껄 쓸 수 없다.
	}

}

Test 3 실행 결과
Test 3 실행 결과

 

Interface(인터페이스)를 구현한 클래스 : TestImpl

Object의 equals 메서드를 Override(오버라이드)함 -> hak과 name이 동일하다면 동일 인물

Override 된 메서드가 없다면 Object의 equals 사용 - 주소 비교 : hak과 name이 동일해도 객체의 주소가

다르기 때문에 다른 인물.

package com.day11;

interface Test { //인터페이스
	
	public int total();
	public void write();
	
}

class TestImpl implements Test { 
	
	private String hak, name;
	private int kor, eng;
	
	public TestImpl() { // 기본 생성자 - 기본생성자는 코딩이 없다.
		//기본생성자를 만들어서 메소드를 만들어서 초기화 하는 방법
	}
	
	public TestImpl(String hak, String name, int kor, int eng) { // 오버로딩된 생성자
		//오버로딩된 생성자를 만들어서 만들자마자 초기화
		this.hak = hak;
		this.name = name;
		this.kor = kor;
		this.eng = eng;
		
	}
	
	public void set(String hak, String name, int kor, int eng) {
		//기본생성자를 만들어서 메서드를 만들어서 초기화 하는 방법
		this.hak = hak;
		this.name = name;
		this.kor = kor;
		this.eng = eng;
	}
 
	@Override
	public int total() {
		return kor + eng;
	}

	@Override
	public void write() {
		
		System.out.println(hak + "," + name + "," + total());
	}
	
	@Override // 메서드 이름을 써서 여기는 override를 굳이 안써도 된다.
	public boolean equals(Object ob) {	// ob1.equals(ob2), TestImpl // Object의 equals를 override한다. 이래서 
						//Object ob = ob2 -> upcast (upcast 주변에는 Object가 있다.)
		boolean flag = false;
		
		if(ob instanceof TestImpl) { //instanceof연산자
			
			TestImpl t = (TestImpl)ob; // downcast
			
			// - Upcast : 상속관계에서 자식클래스의 객체를 부모클래스로 형변환

			// - Downcast : 상속관계에서 부모클래스의 객체를 자식클래스로 형변환
			
			if(this.hak.equals(t.hak) && t.name.equals(this.name)) {
				flag = true;			
			}
		}
		
		return flag;
	}
	
	
}
public class Test4 {

	public static void main(String[] args) {
		
		// TestImpl 대신 Test를 써도된다. why? 부모관계 or 인터페이스 관계이기 때문
		// TestImpl ob1 = new TestImpl("111", "배수지", 80, 90);
		Test ob1 = new TestImpl("111", "배수지", 80, 90);
		TestImpl ob2 = new TestImpl("111", "배수지", 100, 100);
		
		if(ob1.equals(ob2)) {
			System.out.println("ob1과 ob2는 동일인물!");
		} else {
			System.out.println("ob1과 ob2는 동일인물 x ");
		}
		
		ob1.write();
		ob2.write();
	}

}

Test 4 실행 결과
Test 4 실행 결과

 

Interface(인터페이스)는 다중 상속이 가능하다. (인터페이스가 인터페이스를 상속받을 수 있다.)

package com.day11;

import java.util.Scanner;

//인터페이스 - 다중 상속이 가능하다.
interface FruitA {
	
	String Won = "원";
	
	public int getPrice();
	public String getName();
}

interface ItemFruit extends Fruit { //인터페이스가 인터페이스를 상속받을 대는 extends를 쓴다.
	
	public String getItems();
	
}

class Orange implements ItemFruit {

	@Override
	public int getPrice() {
		return 1500;
	}

	@Override
	public String getName() {
		return "오렌지";
	}

	@Override
	public String getItems() {
		return "과일";
	}
	
}

class Apple implements ItemFruit {

	@Override
	public int getPrice() {
		return 2000;
	}

	@Override
	public String getName() {
		return "사과";
	}

	@Override
	public String getItems() {
		return "과일";
	}
	
}
public class Test5 {
	
	//ItemFruit ob = new Orange()
	//ItemFruit ob = new Apple()
	public void packing(ItemFruit ob) {	//아래 packing이 여기로 들어온다,
		
		System.out.println(ob.getItems());
		System.out.println(ob.getName());
		System.out.println(ob.getPrice() + FruitA.Won);
		
	}

	public static void main(String[] args) {
		
		
		Scanner sc = new Scanner(System.in);
		
		Test5 t = new Test5();
		
		System.out.print("1.오렌지  2.사과");// 1 or 2
		int n = sc.nextInt();
		
		if(n==1)
			t.packing(new Orange()); // t의 packing을 호출할건데 
		else if(n==2)
			t.packing(new Apple());
		
		
		int a;
		a = 10;
		System.out.println(a); // 10
		
		int b;
		b = 20;
		System.out.println(b); // 20
		*/
		
		int a;
		a = 10;
		System.out.println(a); //10
		
		a = 20;
		System.out.println(a); // 20
	}

}

Test 5 실행 결과
Test 5 실행 결과


 

 

수업 시간에 추상 클래스와 인터페이스 2가지를 배웠다. 개념이 부족한 거 같아 다른 자료를 찾아보며 두 개를 비교하며

정리를 해봤지만 아직 100% 이해가 안 된다.. 계속 복습을 해야겠다..(필수..!)

반응형

블로그의 정보

무작정 개발

무작정 개발

활동하기