subclass
라고 한다. ( == derived class
, extended class
, child class
) super class
( == base class
, parent class
)라고 한다. is -a
관계가 성립한다. is a
상위클래스 ) extends
를 사용한다. class Vehicle {
..
}
class Car extends Vehicle {
..
}
super()
등을 통해 하위클래스에 생성자를 적용할 수 있다. (아래 super()
설명)public class Practice {
public static void main(String[] args) {
Car c = new Car();
c.printPrivateN();
}
}
class Vehicle {
private int n = 1;
public int getPrivateN() {
return this.n;
}
}
class Car extends Vehicle {
public void printPrivateN() {
int n = super.getPrivateN();
System.out.println(n); // 1출력
}
}
covariant return type
)super
키워드 통해서 부모클래스의 메소드에 접근 가능 하다public class Superclass {
public void printMethod() {
System.out.println("Printed in Superclass.");
}
}
public class Subclass extends Superclass {
// overrides printMethod in Superclass
public void printMethod() {
super.printMethod();
System.out.println("Printed in Subclass");
}
}
super()
or super(parameter list)
public MountainBike(int startHeight,
int startCadence,
int startSpeed,
int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
super()
)class Vehicle2 {
int num = 1;
Vehicle2(int num) {
this.num = num;
}
}
class Car2 extends Vehicle2 {
int num;
String name;
Car2(int num, String name) { //컴파일 에러.
this.num = num;
this.name = name;
}
}
super()
를 호출한다. Vehicle2() 와 같은 인자 없는 생성자가 Vehicle에 없기 때문에 컴파일 에러가 발생한다. class Vehicle2 {
int num = 1;
Vehicle2(int num) {
this.num = num;
}
}
class Car2 extends Vehicle2 {
int num;
String name;
Car2(int num, String name) { //컴파일 ok.
super(num);
this.name = name;
}
}
public class Animal {
public static void testClassMethod() {
System.out.println("The static method in Animal");
}
public void testInstanceMethod() {
System.out.println("The instance method in Animal");
}
}
public class Cat extends Animal {
public static void testClassMethod() {
System.out.println("The static method in Cat");
}
@Override
public void testInstanceMethod() {
System.out.println("The instance method in Cat");
}
public static void main(String[] args) {
Animal animal = new Cat();
Animal.testClassMethod(); //The static method in Animal
animal.testInstanceMethod(); //The instance method in Cat
}
}
메소드의 시그니처
: 메소드 이름, 매개변수 타입과 수) +리턴타입) 이 같아야 한다. class Vehicle3 {
int oil = 100;
public void run() {
oil--;
}
}
class Car3 extends Vehicle3 {
public void run() {
oil -= 2;
}
}
class Vehicle3 {
int oil = 100;
public void run() {
oil--;
}
}
class Car3 extends Vehicle3 {
protected void run() { // 컴파일 에러
oil -= 2;
}
}
class Vehicle3 {
int oil = 100;
public void run() throws IndexOutOfBoundsException{
oil--;
}
}
class Car3 extends Vehicle3 {
public void run() throws IndexOutOfBoundsException, IOException { // 컴파일 에러
oil -= 2;
}
}
class B extends A {
}
class C {
}
class Vehicle3 {
int oil = 100;
public void run() throws IndexOutOfBoundsException{
oil--;
}
public A getObject() {
return new A();
}
}
class Car3 extends Vehicle3 {
public void run() throws IndexOutOfBoundsException {
oil -= 2;
}
public C getObject() { //컴파일 에러
return new C();
}
class Car3 extends Vehicle3 {
public void run() throws IndexOutOfBoundsException {
oil -= 2;
}
public B getObject() { //이상무
return new B();
}
}
public class DynamicMethodDispatch {
public static void main(String[] args) {
Fruit fruit = new Fruit();
Fruit peach = new Peach();
fruit.eat(); //"과일은 맛있어"
peach.eat(); //"복숭아는 맛있어"
}
}
class Fruit {
public void eat() {
System.out.println("과일은 맛있어");
}
}
class Peach extends Fruit {
@Override
public void eat() {
System.out.println("복숭아는 맛있어");
}
}
만약 컴파일 할 때 결정하면(참조타입에 의해 결정되므로) peach.eat() 도 "과일은 맛있어"가 되어야 한다.
런타임에 결정하면 JVM이 참조변수에 저장된 객체를 확인하므로 위와 같은 결과가 나온다.
이에 따라 다형성이 지원된다
abstract
로 선언된 클래스안녕하세요,
추상 클래스와 메서드의 개념은 객체지향설계 개념을 좀 더 익히면 이해하기 쉬워질 거라 생각합니다.
객체지향설계에서는 구현해야할 것들을 객체들로 바라본 후, 객체들 간의 연관 관계를 상속(Is-a), 포함(Has-a)로 풀어낸다는 것이 핵심입니다.
추상 개념은, 객체들 간의 관계를 상속 관계로 정립하면서 생기는, 점차 상위의 클래스를 정의하게 되면서 발생하는 "구체적인 행동 내용은 부모 입장에서 정의할 수 없는 것"을 묘사하기 위해 이용하는 것이라 생각하면 될 것 같습니다.
글에서 설명하신 것과 같이 isRelevant()는 자식 시점에서는 그 행동을 구체적으로 정의할 수 있지만 부모
입장에서는 그럴 수 없지요. 게다가 모든 자식 클래스가 반드시 구현해야할 행동입니다. 그렇다면 이 메소드를 부모 단계에서 추상화하여 "내 시점에선 행동을 특정할 수 없지만 자식 시점에선 반드시 구현되어야 하고 구현될 수 있는 행동이다" 라는 것을 설계 시점에서 명시하는 겁니다.
학교나 여느 책에선 해당 이야기를 이런 예를 들어서 설명합니다. 만일 갖가지 도형 객체를 구현해야 하고, 넓이나 부피, 중심 좌표 등을 제공해줄 수 있게 해야한다고 합시다. 그럼 "원", "사각형", "삼각형" 등의 클래스는 추상화하여 "평면도형"이라는 부모 클래스를 정의하고 상속받게 할 수 있을겁니다. "구", "직육면체", "삼각뿔" 등의 클래스는 "입체도형"이라는 부모 클래스를 정의하고 상속받게 할 수 있을거고요.
입체도형과 평면도형 클래스는 다시 추상화하여 "도형"이라는 부모 클래스를 정의하고 상속 받게 설계하면 추상화를 완료하게 됩니다. 도형->평면도형->원 과 같은 형태로요.
넓이()나 부피() 같은 메소드는 최종 자식 클래스에선 구체적으로 구현해줄 수 있지만 평면도형, 입체도형 클래스 시점에선 이를 구현하기 힘들겁니다. 따라서 이들을 추상 메소드로 선언해두고, 자식 클래스에서 실제로 구현해주는거죠. 또 도형 입장에서는 무게중심 혹은 질량중심의 좌표를 담아두기 힘들죠. 하지만 평면도형 클래스에선 무게중심의 좌표를, 입체도형 클래스에선 질량중심의 좌표를 담아둘 수 있을 것입니다.
이런 일련의 과정을 이해하고 몇 번 연습과 실습을 거치게 되면 본인이 맡은 기능을 구현해나가는 도중에 본인이 정의한 클래스들 간의 공통점을 찾아 추상화한 부모 클래스를 만들고 추상 메소드를 추출해나갈 수 있게 될겁니다. 그리고 이게 객체지향설계의 절반 정도 되는 것 같습니다. 그만큼 간단하지만 중요한 개념입니다.
안드로이드가 그런 관점에서는 참고하기 좋은 운영체제입니다. 자바 자체가 객체지향 개념에 잘 기반해 만든 언어인데 안드로이드 역시 자바를 이용하여 각종 API와 기능들을 객체지향에 기반해 잘 설계했거든요.
추상 메소드가 있을 수도 있고 없을 수도 있다.
abstract void moveTo(double deltaX, double deltaY);
추상메소드가 있는 클래스는 반드시 추상클래스어야만 한다.
추상클래스의 인스턴스는 만들 수 없다.
추상클래스는 하위클래스를 가질 수 있다.
abstract
로 선언되어야 한다. public class Base{
public void m1() {...}
public final void m2() {...}
public static void m3() {...}
public static final void m4() {...}
}
public class Derived extends Base
{
public void m1() {...} // OK, overriding Base#m1()
public void m2() {...} // forbidden
public static void m3() {...} // OK, hiding Base#m3()
public static void m4() {...} // forbidden
}
Final 변수 : 오직 한번만 초기화될 수 있다. 꼭 선언할 때 초기화될 필요는 없다.
- 변수를 final로 선언한다는 것은 어떤 순간에도 그것이 같은 객체를 가리킨다는 의미다.
public class FinalKeyWord {
public static final int[] ints = new int[5];
public static final List<Integer> integerList = new ArrayList<>();
public static void main(String[] args) {
ints[1] = 5;
integerList.add(1);
System.out.println(Arrays.toString(ints)); //[0, 5, 0, 0, 0]
System.out.println(integerList); //[1]
}
}
모든 클래스의 조상이다.
다양한 메서드를 가지고 있다.
메서드1) clone() : 특정 클래스를 복제해서 인스턴스를 생성할 때 사용
public class Clone {
public static void main(String[] args) {
Student s = new Student("Chars");
Student m = s;
m.changeName("Kim");
System.out.println(s.name); // Kim
}
}
class Student {
String name;
Student(String name) {
this.name = name;
}
public void changeName(String name) {
this.name = name;
}
}
사용방법
throws CloneNotSupportedException
public class Clone {
public static void main(String[] args) {
Student student = new Student("Cheol");
student.toString(); // 가능, 접근제어자 public
student.clone() ; // 불가능, 접근제어자 protected
}
}
class Student implements Cloneable {
String name;
Student(String name) {
this.name = name;
}
}
protected
이므로 Object와 다른 패키지에서 쓰고 싶다면 (일반적으로 이에 해당한다. 우리가 정의한 클래스에서 사용하고 싶다면) clone 다시 써준다. class Test {
int x, y;
}
class Test2 implements Cloneable {
int a;
int b;
Test c = new Test();
public Object clone() throws CloneNotSupportedException
{
return super.clone();
}
}
public class Main {
public static void main(String args[])
throws CloneNotSupportedException
{
Test2 t1 = new Test2();
t1.a = 10;
t1.b = 20;
t1.c.x = 30;
t1.c.y = 40;
Test2 t2 = (Test2)t1.clone();
t2.a = 100;
t2.c.x = 300;
System.out.println(t1.a + " " + t1.b + " " + t1.c.x
+ " " + t1.c.y); // 10 20 300 40
System.out.println(t2.a + " " + t2.b + " " + t2.c.x
+ " " + t2.c.y); // 100 20 300 40
}
}
메서드2) equals() : 오버라이딩해서 동등성 정의
메서드3) finalize() : 객체가 소멸될 때 호출되기로 약속된 메소드. 오버라이딩 해서 객체가 소멸될 때 일어나야 할 일을 작성 할 수 있다.
메서드4) getClass() : 클래스 정보를 얻기 위해 사용한다. (getSimpleName(), getSuperclass(), getInterfaces())
void printClassName(Object obj) {
System.out.println("The object's" + " class is " +
obj.getClass().getSimpleName());
}
메서드5) hashCode()
메서드6) toString()