저번 포스팅에서 정리한 내용을 상기시키며 연습문제를 해결해 보겠습니다. 총 4문제입니다.
다음은 도형을 정의한 Shape
클래스이다. 이 클래스를 부모로 하는 Circle
클래스와 Rectangle
클래스를 작성하시오. 이 때, 생성자도 각 클래스에 맞게 적절히 추가해야 한다.
(1) 클래스명 :
Circle
부모 클래스 :Shape
멤버 변수 :double r
== 반지름
(2) 클래스명 :
Rectangle
부모 클래스 :Shape
멤버 변수 :int width
= 폭,int height
= 높이
메서드 :isSquare
라는 이름을 갖고, 정사각형인지 아닌지 알려준다.boolean
타입을 반환하고 매개변수는 없음.
다음과 같은 클래스가 주어짐.
// Shape.java
abstract class Shape {
Point p;
Shape() {
this(new Point(0,0));
}
Shape(Point p) {
this.p = p;
}
abstract double calcArea(); // 도형의 면적을 계산해서 반환하는 메서드
Point getPosition() {
return p;
}
void setPosition(Point p) {
this.p = p;
}
}
class Point {
int x;
int y;
Point() {
this(0,0);
}
Point(int x, int y) {
this.x=x;
this.y=y;
}
public String toString() {
return "["+x+","+y+"]";
}
}
우선 Shape
클래스를 상속받는 Circle
클래스와 Rectangle
클래스를 만들어줍시다.
// Circle 클래스
public class Circle extends Shape {}
// Rectangle 클래스
public class Rectangle extends Shape {}
그리고 Circle
클래스와 Rectangle
클래스에 각각 변수 double r
과 int width
, int height
을 선언해줍니다.
// Circle 클래스
public class Circle extends Shape {
double r;
}
// Rectangle 클래스
public class Rectangle extends Shape {
int width;
int height;
}
그 다음은 생성자를 만들어줘야겠죠?
// Circle 클래스
public class Circle extends Shape {
double r;
public Circle (double r) {
this.r = r;
}
}
// Rectangle 클래스
public class Rectangle extends Shape {
int width;
int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
}
그리고 각각의 클래스에는 도형의 면적을 반환하는 메서드를 추가해줘야 합니다.
Sharp
클래스를 잘 보시면 calcArea()
메서드가 추상 메서드로 선언되어 있습니다. 이미 상속을 해줬으니 오버라이딩으로 끌어다 쓰면 되겠죠?
// Circle 클래스
public class Circle extends Shape {
double r;
public Circle (double r) {
this.r = r;
}
@Override
public double calcArea() {
return r * r * Math.PI;
}
}
원의 넓이는 반지름 x 반지름 x 원주율이기 때문에 반지름으로 선언해준 r
에 원주율(Math.PI
)을 곱해주어 리턴해줍니다.
// Rectangle 클래스
public class Rectangle extends Shape {
int width;
int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public double calcArea() {
return width * height;
}
}
마찬가지로 Rectangle
클래스에도 오버라이딩한 calcArea()
메서드를 사각형 넓이 구하는 공식을 계산하여 리턴해주게 합니다.
마지막으로 Rectangle
클래스에는 정사각형 여부를 판별해주는 isSquared()
메서드를 선언하라고 나와있습니다.
정사각형은 폭과 높이가 같아야 하므로 조건식을 써서 참, 거짓을 반환하게 해줍니다.
// Rectangle 클래스
public class Rectangle extends Shape {
int width;
int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public double calcArea() {
return width * height;
}
public boolean isSquared() {
return width == height ? true : false;
}
}
저는 이렇게 삼항 연산자로 써주었습니다. if문을 쓴다고 한다면
// Rectangle 클래스
public class Rectangle extends Shape {
int width;
int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
@Override
public double calcArea() {
return width * height;
}
public boolean isSquared() {
if(width == height) {
return true;
} else {
return false;
}
}
}
이렇게 작성해 줄 수 있겠죠?
메인 클래스에서 확인해보겠습니다.
// Main.java
public class Main {
public static void main(String[] args) {
Circle c = new Circle(3.0); // double 타입, 반지름이 3인 원
System.out.println(c.calcArea()); // 28.274333882308138
Rectangle ra = new Rectangle(3, 6); // 폭이 3, 높이가 6인 사각형
System.out.println(ra.calcArea()); // 18.0
System.out.println(ra.isSquared()); // false
}
}
다음과 같은 실행결과를 얻도록 Point3D클래스의 equals()를 멤버변수인 x, y, z 의 값을 비교하도록 오버라이딩하고, toString()은 실행결과를 참고해서 적절히 오버라이딩하시오.
다음과 같은 클래스가 주어짐.
// Main.java
public class Main {
public static void main(String[] args) {
Point3D p1 = new Point3D(1,2,3);
Point3D p2 = new Point3D(1,2,3);
System.out.println(p1);
System.out.println(p2);
System.out.println("p1==p2?"+(p1==p2));
System.out.println("p1.equals(p2)?"+(p1.equals(p2)));
}
}
class Point3D {
int x, y, z;
public Point3D(int x, int y, int z) {
this.x=x;
this.y=y;
this.z=z;
}
public Point3D() {
this(0,0,0);
}
public boolean equals(Object obj) {
/*
(1) 인스턴스변수 x, y, z를 비교하도록 오버라이딩하시오.
*/
}
public String toString() {
/*
(2) 인스턴스변수 x, y, z의 내용을 출력하도록 오버라이딩하시오.
*/
}
}
[실행결과]
[1,2,3]
[1,2,3]
p1==p2?false
p1.equals(p2)?true
우선 메인 클래스를 보시면 Point3D
라는 클래스로 각각 p1
, p2
를 인스턴스로 하는 객체를 만들어주었습니다. 이 객체는 매개변수로 세개의 숫자를 받습니다.
그 다음으로 각각 객체를 출력해주고, p1
과 p2
가 같은지(==
) 비교해주는데 이 비교는 객체의 heap 메모리 주소가 같은지 판별해 주는 것입니다.
그리고 euals()
메서드를 만들어서 객체의 내용이 같은지 비교해주는 것이죠.
Point3D
객체는 내부에서 x
, y
, z
를 매개 변수로 받는 생성자가 있고, 매개변수를 받지 않는 생성자는 x
, y
, z
가 각각 0으로 값이 초기화 되어 있습니다.
그 다음 우리는 equals()
메서드와 toStrind()
메서드를 구현해야 합니다.
우선 equals()
메서드부터 구현해 주겠습니다.
public boolean equals(Object obj) {
}
이 메서드는 파라미터로 obj
객체를 받아옵니다. 이 객체는 Object
즉 자바에서 어떠한 객체의 상속도 받지 않는 최상위 객체입니다.
다시 말해서 어떤 객체를 대입하든 다 들어갈 수 있다는 것이죠!
그리고 equals()
메서드는 이미 우리가 각각 다른 메모리에 저장되어 있는 값의 동일 여부를 조회할 때 사용했었습니다. 마찬가지로 boolean
을 리턴해줬죠?
그래서 우리는 내장되어있는 메서드를 오버라이딩해서 우리 입맛에 맞게 바꿔줄겁니다.
@Override
public boolean equals(Object obj) {
}
이 때 equals()
메서드의 역할을 다시 한 번 상기시켜보면 객체 변수 A가 부모객체를 참조하는지 자식객체를 참조하는지에 대해 boolean
값으로 나타내줍니다.
여기서 우리는 p1
과 p2
의 각 값이 같은지 비교해 줄 것입니다.
@Override
public boolean equals(Object obj) {
Point3D p2 = (Point3D)obj;
}
우선 매개변수로 받은 최상위 객체 obj
을 Point3D
객체 형태로 캐스트 변환 시켜줍니다.
그리고 이 안에 들어오는 값이 각각 같으면 true
를 반환해 주는 형태로 작성하면 되겠습니다.
@Override
public boolean equals(Object obj) {
Point3D p2 = (Point3D)obj;
return x == p2.x && y == p2.y && z == p2.z;
}
이렇게 리턴값을 지정해주면 모두 true
일 때만 true
를 반환해 주겠지요?
그리고 toString()
메서드는 단순히 x
, y
, z
를 출력 형식에 맞게만 리턴해주면 됩니다.
단, toString()
메서드도 이미 내장되어 있는 메서드이기 때문에 오버라이딩 해주어야 합니다.
@Override
public String toString() {
return "[" + x + ", " + y + ", " + z + "]"
}
이렇게 해주면 완성입니다.
아래 세 개의 클래스로부터 공통부분을 뽑아서 Unit이라는 클래스를 만들고, 이 클래스를 상속받도록 코드를 변경하시오.
class Marine { // 보병
int x, y; // 현재 위치
void move(int x, int y) { /* 지정된 위치로 이동 */ }
void stop() { /* 현재 위치에 정지 */ }
void stimPack() { /* 스팀팩을 사용한다.*/}
}
class Tank { // 탱크
int x, y; // 현재 위치
void move(int x, int y) { /* 지정된 위치로 이동 */ }
void stop() { /* 현재 위치에 정지 */ }
void changeMode() { /* 공격모드를 변환한다. */}
}
class Dropship { // 수송선
int x, y; // 현재 위치
void move(int x, int y) { /* 지정된 위치로 이동 */ }
void stop() { /* 현재 위치에 정지 */ }
void load() { /* 선택된 대상을 태운다.*/ }
void unload() { /* 선택된 대상을 내린다.*/ }
}
상속은 공통된 요소들끼리 묶어주면 된다고 했습니다. 그러므로 각각의 클래스에서 공통되는 것만 뽑아서 Unit
이라는 클래스에 넣어주면 되겠습니다.
int x, y
와 void move(int x, int y)
, void stop()
이 세개의 항목이 공통되므로 Unit
이라는 클래스를 만들어 넣어줍시다.
// Unit.java
public class Unit {
protected int x, y;
protected void move(int x, int y) {}
protected void stop() {}
}
그리고 상속을 시켜주기 전에 Unit
클래스에 생성자를 만들어줍시다!
// Unit.java
public class Unit {
protected int x, y;
protected void move(int x, int y) {}
protected void stop() {}
// 생성자
public Unit(int x, int y) {
this.x = x;
this.y = y;
}
}
그리고 Marine
, Tank
, Dropship
클래스에 Unit
클래스를 상속시켜 줍니다.
class Marine extends Unit { // 보병
public void stimPack() { /* 스팀팩을 사용한다.*/}
}
class Tank extends Unit { // 탱크
public void changeMode() { /* 공격모드를 변환한다. */}
}
class Dropship extends Unit { // 수송선
public void load() { /* 선택된 대상을 태운다.*/ }
public void unload() { /* 선택된 대상을 내린다.*/ }
}
상속받은 메서드를 오버라이딩 해줍시다. 여기에 생성자까지 추가해주면 더 좋겠죠?
class Marine extends Unit { // 보병
public Marine(int x, int y) {
super(x, y);
}
public void stimPack() { /* 스팀팩을 사용한다.*/}
@Override
protected void move(int x, int y) {}
@Override
protected void stop() {}
}
class Tank extends Unit { // 탱크
public Tank(int x, int y) {
super(x, y);
}
public void changeMode() { /* 공격모드를 변환한다. */}
@Override
protected void move(int x, int y) {}
@Override
protected void stop() {}
}
class Dropship extends Unit { // 수송선
public Dropship(int x, int y) {
super(x, y);
}
public void load() { /* 선택된 대상을 태운다.*/ }
public void unload() { /* 선택된 대상을 내린다.*/ }
@Override
protected void move(int x, int y) {}
@Override
protected void stop() {}
}
인터페이스를 사용해주는 방법도 있습니다.
다음과 같이 인터페이스를 만들어주고 인터페이스에는 추상 메서드만 들어가므로 추상 메서드를 넣어줍니다.
public interface Unit {
public void (int x, int y);
public void stop;
}
그리고 각각의 클래스들은 인터페이스에 변수를 선언할 수 없어서 담아주지 않았기 때문에 자신이 가지고 있어야 합니다.
또한 메서드들은 이미 선언되었기 때문에 오버라이딩 해주면 되겠죠?
// Marine.java
package cls;
import inter.Unit;
public class Marine implements Unit {
private int x, y;
public Marine(int x, int y) {
this.x = x;
this.y = y;
}
void stimPack() {
System.out.println("스팀팩 사용");
}
@Override
public void move(int x, int y) {
System.out.println(x + ", " + y + "로 이동");
}
@Override
public void stop() {
System.out.println("현위치에서 정지");
}
}
// Tank.java
package cls;
import inter.Unit;
public class Tank implements Unit {
private int x, y;
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public void move(int x, int y) {
System.out.println(x + ", " + y + "로 이동");
}
@Override
public void stop() {
System.out.println("현위치에서 정지");
}
public void changeMode() {
System.out.println("공격모드 변환");
}
}
// Dropship.java
package cls;
import inter.Unit;
public class Dropship implements Unit {
private int x, y;
public Dropship(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public void move(int x, int y) {
System.out.println(x + ", " + y + "로 이동");
}
@Override
public void stop() {
System.out.println("현위치에서 정지");
}
public void load() {
System.out.println("load 함");
}
public void unload() {
System.out.println("unload 함");
}
}
저는 빈 메서드에 System.out.println()
을 넣어서 메인 클래스에서 호출했을 때 메시지를 내주게 하였습니다.
다음과 같은 실행결과를 얻도록 코드를 완성하시오.
[Hint] instanceof 연산자를 사용해서 형변환 한다.
[실행결과]
춤을 춥니다.
노래를 합니다.
그림을 그립니다.
메서드명 :
action
기능 : 주어진 객체의 메서드를 호출한다.
DanceRobot
인 경우,dance()
를 호출하고,
SingRobot
인 경우,sing()
을 호출하고,
DrawRobot
인 경우,draw()
를 호출한다.
반환타입 : 없음
매개변수 :Robot r
-Robot
인스턴스 또는Robot
의 자손 인스턴스
다음 클래스가 주어진다
// Main.java
public class Main {
public static void main(String[] args) {
Robot[] arr = { new DanceRobot(), new DrawRobot(), new SingRobot() };
for(int i=0; i<arr.length; i++) {
action(arr[i]);
}
} // main
}
// Robot.java
package cls;
public class Robot { }
// DanceRobot.java
class DanceRobot extends Robot {
public void dance() {
System.out.println("춤을 춥니다.");
}
}
// SingRobot.java
class SingRobot extends Robot {
public void sing() {
System.out.println("노래를 합니다.");
}
}
// DrawRobot.java
class DrawRobot extends Robot {
public void sing() {
System.out.println("그림을 그립니다.");
}
}
우선은 배열안에 객체가 생성됩니다. 그리고 반복문 루프를 돌며 action
이라는 함수에 arr
의 i
번째 인덱스의 요소를 넣고 돌려줍니다.
action
메서드는 객체의 메서드를 호출해야하며, 반환타입이 없는 메서드입니다.
매개변수로 Robot
의 인스턴스 또는 Robot
의 자손 인스턴스를 받습니다.
자 그러면 메인함수 바로 밑에 action
함수를 작성해보겠습니다.
public static void action(Robot robot) { // 매개변수로 Robot의 인스턴스 받음
}
그렇다면 매개변수로 받을 배열안의 각각의 객체가 우리가 가지고 있는 클래스에서 만들어진것이라면 해당 클래스에 있는 메서드를 출력해주면 되겠죠?
어떤 객체가 비교할 객체를 참조한 것인지 비교할 때 우리는 instanceof
를 사용했습니다. 힌트에도 나와있지요?
public static void action(Robot robot) { // 매개변수로 Robot의 인스턴스 받음
if(robot instanceof DanceRobot) {
DanceRobot dr = (DanceRobot) robot;
dr.dance()
}
}
이렇게 매개변수로 받는 객체가 DanceRobot
객체를 참조한다면 robot
을 DanceRobot
으로 강제 변환하여 dr
이라는 변수에 넣어주고 이 dr
의 메서드를 실행시켜줍니다.
나머지도 이렇게 해주면 됩니다.
public static void action(Robot robot) { // 매개변수로 Robot의 인스턴스 받음
if(robot instanceof DanceRobot) {
DanceRobot dr = (DanceRobot) robot;
dr.dance()
} else if (robot instanceof DrawRobot) {
((DrawRobot) robot).draw();
} else if (robot instanceof SingRobot) {
((SingRobot) robot).sing();
}
}
이해 되셨나요??
이번 포스팅에서 준비한 내용은 여기까지 입니다.
복습에 많은 도움이 되었기를 바랍니다!