- static, 상속, 다형성
public class JavaPractice {
public static void main(String[] args) {
검은고양이 a = new 검은고양이();
a.숨쉬다();
a.야옹();
a.뛰어넘다();
a.미래를_예지하다();
흰고양이 b = new 흰고양이();
b.숨쉬다();
b.야옹();
b.뛰어넘다();
b.목숨을_늘리다();
}
}
class 고양이{
void 숨쉬다() {}
void 야옹() {}
void 뛰어넘다() {}
}
class 검은고양이 extends 고양이{
/**
* 고양이 상속을 통해 숨쉬다, 야옹, 뛰어넘다를 선언해주지 않아도 사용할 수 있다.
*/
// void 숨쉬다() {}
// void 야옹() {}
// void 뛰어넘다() {}
void 미래를_예지하다() {}
}
class 흰고양이 extends 고양이{
// void 숨쉬다() {}
// void 야옹() {}
// void 뛰어넘다() {}
void 목숨을_늘리다() {}
}
상속을 사용하였을 때의 이점은 코드의 중복을 피할 수 있으며 당연히 코드도 간략해져서 가독성이 좋아지는 것을 볼 수 있다.
상속, 추상 클래스, 수상 메서드, 인터페이스를 공부하면서 느낀 점은 다 다형성을 위해 만들어진 기능이라 생각된다. 추가로, 객체지향에서의 핵심은 다형성이 아닐까 생각된다.
다형성이란?
"여러 가지 형태를 가질 수 있는 능력"이다. 자바에서는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 해준다. '조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다'는 것이다.
// Tv parent, SmartTv child
Tv t = new SmartTv(); // ok
SmartTv s = new Tv(); // 에러. 허용 안 됨
매개변수의 다형성
장점
1. 다형적 매개변수
2. 여러 종류의 객체를 배열로 다루기
다형적 매개변수
참조형 매개변수는 메세드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스를 념겨줄 수 있다.
class Buyer {
int money = 1000;
int bonusPoint = 0;
void buy (Product p) {
money -= p.price;
bonusPoint += p.bonusPoint;
}
}
// Product가 아니면
// class Tv extends Product {}
void buy (Tv tv)) {
money -= tv.price;
bonusPoint += tv.bonusPoint;
}
// 이런식으로 하나하나 다 만들어야한다.
// 이게 매개변수의 다형성의 1번 장점이다.
여러 종류의 객체를 배열로 다루기
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
class Buyer {
int money = 1000;
int bonusPoint = 0;
Product[] cart = new Product[10]; //구입한 물건을 담을 배열
int i = 0; //장바구니 인덱스
void buy (Product p) {
money -= p.price;
bonusPoint += p.bonusPoint;
cart[i++] = p; //카트에 저장하고 i는 1증가
}
}
인터페이스를 이용한 다형성
interface Fightable {
void move(int x, int y);
void attack(Fightable f); // 상속받아 구현한 클래스의 인스턴스만 가능
}
class Fighter extends Unit implements Figtable {
public void move(int x, int y) { ... }
public void attack(Fightable f) { ... }
}
조상클래스 -> 자손객체
Unit u = new Fighter(); // 가능
Fightable f = new Fighter(); // 가능
- 생성자, 예외처리
이러한 생성자는 다음과 같은 특징을 가집니다.
생성자는 반환값이 없지만, 반환 타입을 void형으로 선언하지 않습니다.
생성자는 초기화를 위한 데이터를 인수로 전달받을 수 있습니다.
객체를 초기화하는 방법이 여러 개 존재할 경우에는 하나의 클래스가 여러 개의 생성자를 가질 수 있습니다.
클래스명과 메서드명이 동일하다.
즉, 생성자도 하나의 메소드이므로, 메소드 오버로딩이 가능하다는 의미입니다.
public class ThisIsJava {
public static void main(String[] args) {
전사 a전사 = new 전사("김질두"); // new 전사() 로 생성하게 되면 홍길동이 나온다.
System.out.println(a전사.이름);
System.out.println(a전사.나이);
}
}
class 전사 {
String 이름;
int 나이;
// 오버로딩
전사(){
이름 = "홍긷롱";
나이 = 20;
}
전사(String 이름){
this.이름 = 이름;
나이 = 15;
}
}
public static void main(String[] args) {
전사 a전사 = new 홍길동();
a전사.a무기 = new 활();
a전사.공격();
// 출력 => 홍길동이(가) 활(으)로 공격합니다.
a전사 = new 홍길순();
a전사.공격();
// 출력 => 홍길순이(가) 칼(으)로 공격합니다.
a전사.a무기 = new 창();
a전사.공격();
// 출력 => 홍길순이(가) 창(으)로 공격합니다.
}
}
abstract class 전사 {
String 이름;
무기 a무기;
public void 공격() {
a무기.작동(이름);
}
}
class 홍길동 extends 전사 {
홍길동() {
이름 = "홍길동";
}
}
class 홍길순 extends 전사 {
홍길순() {
이름 = "홍길순";
a무기 = new 칼();
}
}
abstract class 무기 {
String 무기명;
void 작동(String 사용자명) {
System.out.println(사용자명 + "(이)가 " + 무기명 + "(으)로 공격합니다.");
}
}
class 활 extends 무기 {
활() {
무기명 = "활";
}
}
class 창 extends 무기 {
창() {
무기명 = "창";
}
}
class 칼 extends 무기 {
칼() {
무기명 = "칼";
}
}
class Main {
public static void main(String[] args) {
int[] datas = new int[2];
try {
work(datas);
}
catch ( IllegalArgumentException e ) {
System.out.println("하하");
}
}
static void work(int[] datas) {
if ( datas.length < 3 ) {
throw new IllegalArgumentException(); // 함수가 여기서 멈춤
}
datas[0] = 10;
datas[1] = 20;
datas[2] = 30;
}
}
자바에서 기본적으로 예외처리를 제공하지만 모든 사용자를 만족시킬 수 없다. 그래서 커스텀으로 예외처리를 할 수 있는데 아래 내용이 바로 커스텀으로 예외처리를 한 것이다.
class Main {
public static void main(String[] args) {
int[] datas = new int[2];
try {
work(datas);
}
catch ( 입력된_배열의_사이즈가_3보다_작은_Exception e ) { // 코드 가독성이 v2 보다 좋음
System.out.println("하하");
}
}
static void work(int[] datas) {
if ( datas.length < 3 ) {
throw new 입력된_배열의_사이즈가_3보다_작은_Exception(); // 함수가 여기서 멈춤
}
datas[0] = 10;
datas[1] = 20;
datas[2] = 30;
}
}
class 입력된_배열의_사이즈가_3보다_작은_Exception extends RuntimeException {
// 예외처리 내용
}