5/7 생활코딩 JAVA - 오버로딩, 메소드 시그니처, 접근제어자, 추상, final, 인터페이스, 다형성, 예외처리, Object, equals, 상수/enum
5/7 공부내용
커리큘럼: 오버로딩(복습) ~ 상수, Enum
- 원래 오늘 꼭 끝까지 정주행하고 싶었는데 input량이 너무 많다. 무리해서라도 들으라고 하면 들을지 모르겠지만, 전혀 내게 도움이 될 것 같지 않다. 내일은 마무리 지을 수 있을 것같다.
- 여태 배우면서 끄덕끄덕하고 다 이해되던 것들이 많았지만, 지금은 좀 복잡한게 많다. 오늘 복습 노트 제대로 읽어봐야할 것 같다.
1) 오버로딩이란? 오버로딩 함수를 정의 하는 법?
자바는 매개변수 타입이나 갯수가 다르다면 다른 함수로 인식한다. 메소드 이름이 같지만 input parameter에 따라 메소드가 다르게 동작하는 방식을 오버로딩이라고 한다.
왜 오버로딩을 쓸까? 매번 같은 취지의 함수를 새로운 이름으로 만들어주면 사용자 입장에선 헷갈리고 코드 양은 많아진다.
만약 하나의 메소드가 매개변수의 속성에 따라 알맞는 메소드를 실행시켜줄 수 있으면 이 문제를 해결해줄 수 있기 때문에 유용하다. 오버로딩할 때 주의점은 매개변수의 이름과 리턴값(형식)은 반드시 같아야 하며 매개변수는 달라야한다는 것이다. (매개변수의 이름은 상관 없다)
2) 메소드의 시그니처란?
메소드의 이름, 매개변수의 형식, 리턴타입
public static void A(int a); {
System.out.println("Hello World!")
}
에서
A() : 메소드의 이름 / int a : 매개변수의 형식 / void : 리턴타입
상위클래스에서 기본적인 본체의 모습까지 정의해두고, 사용자가 그 기본을 사용하던지 혹은 오버라이딩을 하던지 하면 굳이 추상화라는 개념이 등장하지 않아도 되는게 아닌지 하는 의문이 있었는데, 이것은 객체지향의 본질과도 같은 개념이라 할 수 있겠다. 사실 어떤 클래스나 메소드가 실제로 어떻게 동작하는지도 개발자 입장에선 중요하지만, 클래스의 선언부가 중요하다. abstract라는 키워드를 추가함으로서 자신이 반드시 상속받아야 하는 메소드가 무엇인지 조금 더 경각심을 갖게 되는 효과도 있다. 객체지향적인 모습이랄까.
위에 언급한 toString처럼 equals 역시 Object의 메소드이다.
어떤 이유에서 사용자는 본인이 정의한 클래스타입의 인스턴스를 Object 형으로 변형했다. 하지만 사용자는 사용자가 정의했던 클래스가 가진 멤버와 Object 클래스의 인스턴스의 멤버를 비교하고 싶다.
그럼 자식->부모 로 이미 변환이 된 것인데, 또 부모->자식으로 바꾸어야 하는 상황이다. (부모클래스에서 바로 자식 클래스의 멤버를 비교대조할 수는 없기 때문에.)
이 때 형 변환을 다시 해줘야 하는데 부모->자식으로 변형이 되려면
이 복잡한 코드가
커리큘럼: 오버로딩(복습) ~ 상수, Enum
- 원래 오늘 꼭 끝까지 정주행하고 싶었는데 input량이 너무 많다. 무리해서라도 들으라고 하면 들을지 모르겠지만, 전혀 내게 도움이 될 것 같지 않다. 내일은 마무리 지을 수 있을 것같다.
- 여태 배우면서 끄덕끄덕하고 다 이해되던 것들이 많았지만, 지금은 좀 복잡한게 많다. 오늘 복습 노트 제대로 읽어봐야할 것 같다.
1) 오버로딩이란? 오버로딩 함수를 정의 하는 법?
자바는 매개변수 타입이나 갯수가 다르다면 다른 함수로 인식한다. 메소드 이름이 같지만 input parameter에 따라 메소드가 다르게 동작하는 방식을 오버로딩이라고 한다.
왜 오버로딩을 쓸까? 매번 같은 취지의 함수를 새로운 이름으로 만들어주면 사용자 입장에선 헷갈리고 코드 양은 많아진다.
만약 하나의 메소드가 매개변수의 속성에 따라 알맞는 메소드를 실행시켜줄 수 있으면 이 문제를 해결해줄 수 있기 때문에 유용하다. 오버로딩할 때 주의점은 매개변수의 이름과 리턴값(형식)은 반드시 같아야 하며 매개변수는 달라야한다는 것이다. (매개변수의 이름은 상관 없다)
2) 메소드의 시그니처란?
메소드의 이름, 매개변수의 형식, 리턴타입
public static void A(int a); {
System.out.println("Hello World!")
}
에서
A() : 메소드의 이름 / int a : 매개변수의 형식 / void : 리턴타입
3) 접근제어자의 효용?
접근제어자는 자기 자신이 아닌 다른 클래스에서 자신의 멤버로 접근할 수 있는 '공적인 접근'과 자신 클래스만이 할 수 있는 '사적인 접근'을 나누는 개념이다. 왜 나눌까?
만약 공적인 자원(멤버)를 아무나 침범할 수 있다면 자원에 문제가 생겼을 때 해당 자원에 엑세스 하는 모든 주체들이 혼란스러워질 수 있거나 그 자원의 값이 달라져서 통일성이 없어질 수 있다. 그리고 중요한 자원의 기밀이 유지되어야 하는 경우에 다른 곳에서 접근하면 기밀이 유출될 가능성도 있기 때문이다.
접근제어자는 4종류가 있다. public, protected, default, private이 있는데 세밀한 것은 졸려서 제대로 듣지 못했으므로 나중에 볼 일이 생긴다면 https://www.youtube.com/watch?v=7hSC5k6zgxA를 참고하자.
만약 공적인 자원(멤버)를 아무나 침범할 수 있다면 자원에 문제가 생겼을 때 해당 자원에 엑세스 하는 모든 주체들이 혼란스러워질 수 있거나 그 자원의 값이 달라져서 통일성이 없어질 수 있다. 그리고 중요한 자원의 기밀이 유지되어야 하는 경우에 다른 곳에서 접근하면 기밀이 유출될 가능성도 있기 때문이다.
접근제어자는 4종류가 있다. public, protected, default, private이 있는데 세밀한 것은 졸려서 제대로 듣지 못했으므로 나중에 볼 일이 생긴다면 https://www.youtube.com/watch?v=7hSC5k6zgxA를 참고하자.
4) 추상메소드? 추상클래스?
추상메소드는 상위 클래스에서 정의한다. 사용하는 사람이 반드시 오버라이딩 해서 메소드를 정의해주어야 한다. 추상 메소드를 하나라도 가진 클래스는 추상 클래스(abstract class)가 되어야 한다. 추상클래스 내에서도 추상 메소드가 아닌 일반 메소드 (본체를 가진 메소드)가 존재할 수 있다.
그렇다면 이걸 왜 쓰는가? 메소드는 정의되어 있지만, 사용자별로 그 메소드를 어떻게 활용할지는 천차만별로 다를 수 있다. 따라서 클래스 안에서 어떤 메소드를 사용할 수 있는지만 사용자에게 notify를 해주고 구체적으로 어떤 것을 할지는 사용자에게 위임하는 것이다.
상위클래스에서 기본적인 본체의 모습까지 정의해두고, 사용자가 그 기본을 사용하던지 혹은 오버라이딩을 하던지 하면 굳이 추상화라는 개념이 등장하지 않아도 되는게 아닌지 하는 의문이 있었는데, 이것은 객체지향의 본질과도 같은 개념이라 할 수 있겠다. 사실 어떤 클래스나 메소드가 실제로 어떻게 동작하는지도 개발자 입장에선 중요하지만, 클래스의 선언부가 중요하다. abstract라는 키워드를 추가함으로서 자신이 반드시 상속받아야 하는 메소드가 무엇인지 조금 더 경각심을 갖게 되는 효과도 있다. 객체지향적인 모습이랄까.
5) final이란?
고정시켜 주는 것이다.
변수에게 final을 준다면 - 변수값을 다른 클래스나 메소드에서 변경시킬 수 없다.
메소드에게 final을 준다면 - 다른 클래스에서 오버라이딩을 할 수 없는 메소드가 된다.
클래스에게 final을 준다면 - 다른 클래스에서 상속할 수 없는 클래스가 됨. (상속이 금지 됨)
변수에게 final을 준다면 - 변수값을 다른 클래스나 메소드에서 변경시킬 수 없다.
메소드에게 final을 준다면 - 다른 클래스에서 오버라이딩을 할 수 없는 메소드가 된다.
클래스에게 final을 준다면 - 다른 클래스에서 상속할 수 없는 클래스가 됨. (상속이 금지 됨)
6) 인터페이스
인터페이스란?
어떤 클래스가 구현될 때 반드시 정의되어야 하는 면적이라고 생각하자. Interface 라고 정의한 항목들은 해당 인터페이스를 구현하는 클래스가 반드시 정의해줘야 한다. 정의하지 않으면 컴파일 조차 되지 않는다.
클래스 A는 인터페이스 I를 구현한다 의 코드는?
class A implements interface I 이다.
어떻게 보면 인터페이스는 abstract와 비슷한 면을 가지고 있는 것 같다.
인터페이스란?
어떤 클래스가 구현될 때 반드시 정의되어야 하는 면적이라고 생각하자. Interface 라고 정의한 항목들은 해당 인터페이스를 구현하는 클래스가 반드시 정의해줘야 한다. 정의하지 않으면 컴파일 조차 되지 않는다.
클래스 A는 인터페이스 I를 구현한다 의 코드는?
class A implements interface I 이다.
어떻게 보면 인터페이스는 abstract와 비슷한 면을 가지고 있는 것 같다.
@테스트 해볼 항목이 있다.
인터페이스에서 정의한 멤버들은 모두 정의한채로 추가 메소드를 정의하면 에러가 발생하는지이다.
인터페이스에서 정의한 멤버들은 모두 정의한채로 추가 메소드를 정의하면 에러가 발생하는지이다.
여담: 회의에도 인터페이스 같은 기능이 하나 있으면 좋겠다. who what when이 늘 마지막에 도출되고 템플릿화 된다면 좋을 것 같다.
7) 다형성
다형성은 많은 모습을 가진 속성이라고 굳이 해석할 수 있겠다. 쉽게 말해 오버로딩을 떠올리면 된다. 같은 메소드임에도 다른 동작방식을 갖는다. A obj = new B(); 인 경우 A를 상속한 B클래스의 인스턴스를 obj라는 변수에 배정하는데 이 변수는 A클래스의 모습을 하고 있다. 만약 A클래스에서 x라는 메소드를 정의하고 B클래스에서 y라는 메소드를 정의했을 때 위에 obj는 y라는 메소드를 obj.y()라는 명령어로 사용할 수 있을까? 그렇지 않다. obj는 B의 인스턴스이지만 A클래스의 행세를 하고 있기 때문이다. A클래스엔 y메소드가 정의되어있지 않고 그 때문에 obj.y()는 동작하지 않는다.
그런데 만약 x라는 메소드를 B가 오버라이딩 해서 obj.x()를 호출한다면!? 신기하게도 B클래스에서 오버라이딩 한 obj.x()의 결과값이 출력된다.... 흠터레스팅.
그럼 이런 복잡한 룰은 대체 왜!??!
그럼 이런 복잡한 룰은 대체 왜!??!
부모의 클래스로부터 상속 된 많은 클래스들이 있을 것이다. 자바는 인스턴스 자체를 파라미터로 넣을 때, 그 파라미터의 데이터타입까지(클래스명)도 같이 전달해주어야 한다. 근데 만약 A라는 클래스에서 파생 된 Aa와 Ab의 클래스가 공통으로 갖고 있는 함수 run을 실행해주어야 한다면? 파라미터의 데이터타입이 다르다는 이유로 (Aa instance), (Ab instance)를 처리하는 코드를 각각 만들어주어야 한다. 이는 코드의 중복을 낳게 되므로, 아예 공통점이 있는 부모 클래스명을 쓰면 코드 중복을 제거할 수 있고 이 때문에 부모클래스명을 데이터타입으로 하는 것이다. 즉 같은 데이터타입을 가졌음에도 다른 메소드를 실행할 수 있어 다형성의 범주에 속할 수 있다.
이것은 인터페이스도 적용되는 이야기이다. 인터페이스 I를 구현한 클래스 A, B가 있다고 가정하자. A, B로부터 생겨난 각각의 인스턴스들은 I라는 데이터 타입을 가진 각각 변수 a, b에 할당되었다. a, b클래스는 각각 A, B가 구현하는 모든 멤버들을 준비하지 않고 인터페이스에서 정의한 메소드만을 실행시킴으로서 권한 및 사용제한 컨셉을 가질 수 있게 된다. (아빠 프로그래머/신자/낚시광, 엄마 프로그래머/바리스타) 예제를 다시금 떠올려 보자.
다형성을 비유를 들어 쉽게 이해해보자.
준이라는 아이가 있다. 준이 엄마는 준이에게 심부름을 시켰다. 그런데 준이 모습으로는 할 수 없는 일도 심부름에 포함되어 있어서 준이에게 겉모습이 바뀌는 마법을 시전했다. (obj라는 인스턴스가 A라는 클래스의 데이터 타입을 지님) 준이는 심부름을 하러 갔고 어머니가 시킨 task를 수행하기 위해 지은이 이모를 찾아갔다. 지은이 이모는 준이를 준이 엄마인 줄 알았고 준이는 엄마가 시킨 심부름, 지은 이모에게 돈 주기를 실행했다. (부모클래스에 정의 된 메소드 실행) 하지만 그 순간, 옆에 스크류바 맛 사탕이 보인다. 준이는 너무나 사고 싶었다. 스크류바를 사려고 하는 순간, 몸이 뜻대로 움직이지 않는다. 엄마의 심부름 task 목록에 '물건 사기' 행위는 없기 때문이었다. (부모클래스에 정의되지 않은 메소드는 자식이 새롭게 정의한다 해도 수행되지 않음). 그래서 오류가 나 버렸고 준이는 지은 이모에게 돈만 전해주고 왔다. 그러나 준이는 집에 돌아와서 꾸중을 듣고 다시 지은이 이모를 찾아갔다. 왜일까? 바로 돈을 받을 대상은 지은 이모가 아니라 리아 이모였기 때문이다. 그럼 Task 목록에 들어있지 않았을텐데 어떻게 준이는 지은 이모에게 돈을 줄 수 있었을까? 대답은 준이 엄마가 '돈 전달하기'를 task로 삼았기 때문이다. 준이는 엄마 명령을 받아 이모에게 돈을 전달해줬지만 실수로 자기가 임의로 그 task를 바꿔서 지은 이모에게 돈을 줘버렸고 (오버라이딩) 이는 준이 엄마가 시킨 task를 약간 변형했던 것이기 때문에(부모클래스에서 정의한 메소드는 자식이 오버라이딩할 경우 자식이 정의한대로 수행됨) 동작이 가능했기 때문이다.
8) 예외처리
try, catch, finally의 문법이 있다.
finally는 실행성공/실패여부에 관계없이 무조건 실행되는 메소드이다.
finally는 실행성공/실패여부에 관계없이 무조건 실행되는 메소드이다.
예외처리는 짬 때릴 수 있다. 메소드명 옆에 throw blahblahException 이라고 하면 해당 메소드를 호출해서 사용하는 사용자에게 예외처리 방법을 넘길 수 있다.
checked exception, unchecked exception은 무엇이고 어떤 차이가 있는가?
모든 예외처리는 Exception이라는 클래스의 하위에 존재한다. RuntimeException 하위에 있다면 unchecked, 그렇지 않다면 checked이다. unchecked는 예외처리가 강제되지 않는 것이고, checked는 예외처리를 반드시 해주어야 하는 것이다.
unchecked - 강제라고 체크되지 않은.
checked - 강제라고 체크 된.
9) Object는?
사실 자바는 클래스 작성 - 인스턴스화 - 실행의 패턴을 거친다. 이 모든 클래스의 조상이 되는 클래스가 바로 Object라는 클래스다. 따라서 우리가 정의하는 모든 클래스는 사실 뒤에 extends Object < 라는 문구가 생략되어 있는 것이고, 자식클래스는 부모클래스의 메소드를 사용할 수 있기 때문에 Object의 메소드를 사용할 수 있다.
대표적인 예가 toString 메소드인데 이 메소드에는 비밀이 있다. 바로 어떤 객체를 그냥 프린트 하거나 객체.toString을 프린트 해도 똑같은 결과를 낸다는 것이다.
toString이 부모클래스(Object)의 메소드이고 자식이 사용할 수 있는 것은 알겠다. 근데 왜 toString이라는 메소드를 써도 결과가 같은 것일까? 그건 바로 객체를 그냥 print하면 이 때 메소드 뒤에 toString이 자동으로 붙어서 실행되기 때문이다.
사실 자바는 클래스 작성 - 인스턴스화 - 실행의 패턴을 거친다. 이 모든 클래스의 조상이 되는 클래스가 바로 Object라는 클래스다. 따라서 우리가 정의하는 모든 클래스는 사실 뒤에 extends Object < 라는 문구가 생략되어 있는 것이고, 자식클래스는 부모클래스의 메소드를 사용할 수 있기 때문에 Object의 메소드를 사용할 수 있다.
대표적인 예가 toString 메소드인데 이 메소드에는 비밀이 있다. 바로 어떤 객체를 그냥 프린트 하거나 객체.toString을 프린트 해도 똑같은 결과를 낸다는 것이다.
toString이 부모클래스(Object)의 메소드이고 자식이 사용할 수 있는 것은 알겠다. 근데 왜 toString이라는 메소드를 써도 결과가 같은 것일까? 그건 바로 객체를 그냥 print하면 이 때 메소드 뒤에 toString이 자동으로 붙어서 실행되기 때문이다.
10) equals를 내 기호에 따라 true, false 값이 반환되도록 하는 방법?
위에 언급한 toString처럼 equals 역시 Object의 메소드이다.
어떤 이유에서 사용자는 본인이 정의한 클래스타입의 인스턴스를 Object 형으로 변형했다. 하지만 사용자는 사용자가 정의했던 클래스가 가진 멤버와 Object 클래스의 인스턴스의 멤버를 비교하고 싶다.
그럼 자식->부모 로 이미 변환이 된 것인데, 또 부모->자식으로 바꾸어야 하는 상황이다. (부모클래스에서 바로 자식 클래스의 멤버를 비교대조할 수는 없기 때문에.)
이 때 형 변환을 다시 해줘야 하는데 부모->자식으로 변형이 되려면
'자식클래스데이터타입 인스턴스명 = (자식클래스명) 인스턴스명' 으로 작성한다.
자바에선 자식->부모의 형 변환은 암시적으로 아무런 문제 없이 가능하게 해주지만 부모->자식의 형 변환은 사용자가 명시적으로 기재해주어야 하기 때문이다.
자 그럼 이제 비교/대조할 수 있는 준비는 끝난 것이다.
쉽게 말하면 레베루/체급을 똑같이 맞춰준 것이고, 인스턴스.메소드 == 인스턴스.메소드 << 방식으로 비교해줘서 true를 뱉어낼 것인지 false를 뱉어낼 것인지 return값을 직접 입력해주면 내 기호에 따라 equals가 뱉어내는 값을 정의할 수 있다.
쉽게 말하면 레베루/체급을 똑같이 맞춰준 것이고, 인스턴스.메소드 == 인스턴스.메소드 << 방식으로 비교해줘서 true를 뱉어낼 것인지 false를 뱉어낼 것인지 return값을 직접 입력해주면 내 기호에 따라 equals가 뱉어내는 값을 정의할 수 있다.
@추가내용
이것은 야매 방식이라고 한다. 진짜 제대로 equals를 구현하려면 hascode()라는 메소드 또한 알아야 한다고 하는데 이 부분은 머리 한 구석에 잠깐 넣어두었다가 나중에 때가 되면 끄집어 내도록 하자.
12) 상수, enum
상수란 변하지 않는 값이라는 것을 알고 있다. switch문 같은 경우엔 조건으로 받는 데이터 형식에 제한이 있다. Primitive data type(String, int, char, short등..)만 받기 때문에 내가 정의한 클래스를 조건으로 받진 못한다. 이를 해결해줄 수 있는 문제가 바로 enum이다.
enum은 열거형이며, 열거형은 서로 연관 된 상수들의 집합이다.
class
Fruit{
public
static
final
Fruit APPLE =
new
Fruit();
public
static
final
Fruit PEACH =
new
Fruit();
public
static
final
Fruit BANANA =
new
Fruit();
이 복잡한 코드가
enum
Fruit{
APPLE, PEACH, BANANA;
}
이렇게 간략해져버린다.
또 enum은 그 자체가 클래스 속성도 있기 때문에 스스로 클래스 멤버를 지정할 수 있다. 따라서 비슷한 아키텍쳐, 속성을 가진 여러 인스턴스들을 한번에 만들어 버릴 수 있는 큰 장점이 있다.
댓글
댓글 쓰기