211028(3) Java - 추상클래스
추상 클래스
하나 이상의 추상메소드를 포함하는 클래스이다.
추상 메소드 : 자식 클래스에서 반드시 오버라이딩을 해야만 사용할 수 있는 메소드임. 선언부는 있고 구현부는 없어서 재정의를 해줘야한다. 반드시 사용해야 할 메소드를 추상 클래스에 추상메소드로 선언을 하면 이 클래스를 상속 받는 모든 클래스에서는 이 추상 메소드를 반드시 재정의 해야함.
Car에 대한 클래스에 gearType, door, color 모두 지정을 해놨다. 거기에 추상메소드로 "차마다 오디오 시설은 다르다? 반드시 그걸 네가 지정해야 돼"라며 정의를 해둔다.
추상클래스는 추상메소드를 한 개 이상은 꼭 포함하고 있어야한다. 그 외에는 일반 클래스와 모든 점이 같다. 그러니까 abstract 키워드와 추상메소드 한 개 이상을 포함한 것 뿐임. 즉 생성자와 필드, 일반 메소드도 포함할 수 있다. (이런 점이 인터페이스와 다른 점이다!!) abstract 키워드는 클래스와 메소드에 포함이 되어야 한다.
큰 프로젝트 들어가기 전에 어떤 것들을 만들 것인지 뼈대를 잡는 것이라고도 생각할 수 있다. 많은 사람들이 작성하게 되면 1-2개씩 메소드를 빠뜨릴 수 있는데 이렇게 추상클래스를 미리 만들게 되면 단 한 개라도 빠지는 순간 에러가 뜨기 때문에 빠뜨리지 않고 작성할 수 있게 되는 것이다!!
package sample;
public abstract class Animal
{
protected String name; //동물이름
public Animal(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
//어떻게 우는지(추상메서드)
public abstract String getCry();
//어떻게 움직이는지(추상메서드)
public abstract String getMove();
//어떤 것을 먹는지(추상메서드)
public abstract String getFood();
//이런것들을 기본적으로 구현해 그리고 출력해~
//출력(추상메서드)
public abstract void print();
}
선언부는 접근지정자 / abstract / class / 클래스명으로 들어간다.
protected String name;
같은 클래스와 상속 받은 다른 클래스에서 접근 가능한 변수이다.
매개변수가 name인 생성자를 만드는 이유는 반려동물의 경우 이름은 다르게 짓기 때문이다. 생성자로 받아주는 이유도 일반 변수가 아니라 protected 접근제한자를 가지고 있기에 생성자를 통해서 세팅해준다. 그리고 그것을 아래의 getName에서 받아 넘겨준다.
추상클래스는 하나 이상의 추상메소드를 갖고 있어야하기 때문에 아래에는 반드시 사용해야만 하는 추상 메소드들을 정의한다. 정의만 해두고 실제로 값을 넣어주는 것은 자식 클래스에서 하게 됨
package sample;
public class Dog extends Animal
{
//추상메서드를 무조건 정의해야한다.
//울음
private String cry;
//움직임
private String move;
//먹이
private String food;
public Dog(String name)
{
this(name, "멍멍", "껑츙껑츙", "사료");
}
public Dog(String name, String cry, String move, String food)
{
super(name); //부모 클래스에서 매개변수 부분을 보면 안다.
this.cry = cry;
this.move = move;
this.food = food;
}
//어떻게 우는지(추상메서드)
public String getCry()
{
return cry;
}
//어떻게 움직이는지(추상메서드)
public String getMove()
{
return move;
}
//어떤 것을 먹는지(추상메서드)
public String getFood()
{
return food;
}
//이런것들을 기본적으로 구현해 그리고 출력해~
//출력(추상메서드)
public void print()
{
System.out.println("Dog { name : " + name + ", cry : " + cry + ", move : " + move +
", food : " + food + "}");
}
public void setCry(String cry)
{
this.cry = cry;
}
public void setMove(String move)
{
this.move = move;
}
public void setFood(String food)
{
this.food = food;
}
//추상클래스를 애니멀 메소드를 만들었음
//상속을 받는 쪽에서 반드시 재정의를 해줘야함 (오버라이딩)
//애니멀 클래스에서 상속을 받음
//하나라도 빠져있으면 클래스명에서 오류가 뜸 (추상클래스에서 정의했던 추상메소드가 없기 때문에)
}
여기서는 Dog 클래스만 만들었지만 실제로는 많은 클래스들을 만들게 된다. 처음엔 extends로 Animal 클래스를 상속 받아야 추상메소드를 사용할 수 있다. 내가 앞으로 재정의할 메소드를 private 캡슐화 시켜서 변수설정부터 해준다. 마찬가지로 생성자는 name을 넣어서 정의를 해주는데 위에서 정의한 메소드가 반드시 모두 들어가야한다. 밑에 있는 get(), set()도 마찬가지로 부모 클래스(추상클래스)에서 지정한 메소드들이 들어가야 한다.
-추상 메소드를 만들 때 해줘야 할 것들-
private 변수선언
객체 생성시 인자값 받아들임 (다른 것들은 초기화가 돼 있음)
추상메소드 만들기 + 출력
set() · get() 메소드 만들기
view()메소드와 메인클래스
package sample;
public class AnimalMain {
public AnimalMain()
{
//원래는 생성자가 없어도 되지만 view 메소드를 이용하기 위해서는 만들어야함
}
public void view(Animal animal) //메소드
{
if(animal != null)
{
if(animal instanceof Cat)
{
System.out.println("- 동물 : Cat");
}
//애니멀은 뉴 캣 가능함 뒤가 자식이니까
else if(animal instanceof Bird)
{
System.out.println("- 동물 : Bird");
}
else if(animal instanceof Dog)
{
System.out.println("- 동물 : Dog");
}
else
{
System.out.println("정의하지 않았습니다.");
}
System.out.println("이름 : " + animal.getName());
System.out.println("울음 : " + animal.getCry());
//구현부의 경우 자식 클래스이겠지만 선언부는 애니멀이니까 부모클래스에 들어있음
System.out.println("움직임 : " + animal.getMove());
System.out.println("먹이 : " + animal.getFood());
}
}
//view 메소드를 쓰려면 객체생성을 써야한다.
//모두가 사용할 수 있다.
public static void main(String[] args) {
Cat cat = new Cat("나비");
Dog dog = new Dog("누렁이");
Bird bird = new Bird("짹짹이");
cat.print();
dog.print();
bird.print();
System.out.println("===================== 수정후 ========================");
cat.setCry("어흥~");
cat.print();
bird.setMove("날긔♡");
bird.print();
dog.setFood("닭고기 말린 것");
dog.print();
System.out.println("===================== 절취선 ========================");
AnimalMain animalMain = new AnimalMain();
animalMain.view(cat);
animalMain.view(dog);
animalMain.view(bird);
}
}
Cat { name : 나비, cry : 야옹야옹, move : 사뿐사뿐, food : 생선} Dog { name : 누렁이, cry : 멍멍, move : 껑츙껑츙, food : 사료} Bird { name : 짹짹이, cry : 끼룩~, move : 펄럭펄럭, food : 애벌레} ===================== 수정후 ======================== Cat { name : 나비, cry : 어흥~, move : 사뿐사뿐, food : 생선} Bird { name : 짹짹이, cry : 끼룩~, move : 날긔♡, food : 애벌레} Dog { name : 누렁이, cry : 멍멍, move : 껑츙껑츙, food : 닭고기 말린 것} ===================== 절취선 ======================== - 동물 : Cat 이름 : 나비 울음 : 어흥~ 움직임 : 사뿐사뿐 먹이 : 생선 - 동물 : Dog 이름 : 누렁이 울음 : 멍멍 움직임 : 껑츙껑츙 먹이 : 닭고기 말린 것 - 동물 : Bird 이름 : 짹짹이 울음 : 끼룩~ 움직임 : 날긔♡ 먹이 : 애벌레 |
view메소드의 기능?
instanceof 연산자를 이용하기 위해서 만듦.. instanceof 연산자의 경우 참조변수가 참조하고 있는 인스턴스 실제 타입을 확인하는 기능을 가지고 있다.
문법) 참조변수 / instanceof 클래스이름 -> true, false 값을 리턴
view메소드 출력부분에서 상속 받은 자식클래스(dog. & cat. & bird 등)가 아니라 부모클래스인 animal.get()을 사용한 이유는 추상클래스의 경우 구현부는 자식에서 작성을 했지만 최초의 선언은 애니멀에서 해주었기 때문에 부모클래스에 들어가 있기 때문이다.
Main 클래스 부분
자식클래스 객체생성을 하면서 name 인자값을 던져준다.
각각의 프린트문을 출력한다.
수정을 할 값이 있을 때는 cat.setCry("어흥~"); 이런식으로 바꾸어준다.
animalMain.view(cat); 메소드 출력을 하면 각 instanceof 타입(던져준 인자값)에 따라서 맞는 메소드의 결과가 출력된다.