[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();
}
}
상속 관계가 아닌 클래스의 경우 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
}
}
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()); 오류 - 부모는 자식껄 쓸 수 없다.
}
}
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();
}
}
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
}
}
수업 시간에 추상 클래스와 인터페이스 2가지를 배웠다. 개념이 부족한 거 같아 다른 자료를 찾아보며 두 개를 비교하며
정리를 해봤지만 아직 100% 이해가 안 된다.. 계속 복습을 해야겠다..(필수..!)
반응형
'Language > Java' 카테고리의 다른 글
[JAVA+국비교육] ArrayList, List, Map, Generic (0) | 2022.01.07 |
---|---|
[JAVA+국비교육] 내부클래스(익명의, 무명의, Anonymous클래스), Vector (0) | 2022.01.06 |
[JAVA+국비교육] Wrapper 클래스, Calendar 클래스(만년달력), 추상클래스, final (0) | 2022.01.04 |
[JAVA+국비교육] 상속, Object 클래스, 생성자, Override, super(), 클래스를 활용해서 계산기 만들기 (0) | 2022.01.03 |
[JAVA+국비교육] 상속, Call By Value, Call By Reference (0) | 2022.01.01 |
블로그의 정보
무작정 개발
무작정 개발