본문출처:

https://medium.com/@yjw113080/javascript%EC%9D%98-%EB%91%90-%EC%B6%95-310fc1091079

 

JavaScript의 두 축

Part 1: 프로토타입형 상속, 클래스 상속의 지옥불 탈출하기

medium.com

 


Javascript는 가장 중요한 프로그래밍 언어 중 하나입니다. 단순히 인기가 많아서가 아니라, 프로그래밍의 발전 역사에 굉장히 중요한 두 개의 패러다임을 차용하고 있기 때문이죠.

  • 프로토타입형 상속(클래스 없는 객체, 프로토타입 위임(OLOO — Objects Linking to Other Objects)),
  • 함수형 프로그래밍(클로저를 동반하는 람다형 함수로 구현)

이 둘을 합쳐서 JavaScript의 두 축(the two pillars of JavaScript)이라고 합니다. JS는 가장 영향력 있는 프로그래밍 언어 중 하나인 이유는 바로 이 때문입니다. 다른 많은 언어들이 이 두 가지중 하나, 혹은 모두를 가져가서 자기 만의 버전을 만들었습니다. 우리가 어플리케이션을 개발하는 방법, 내지 프로그래밍 언어라는 것 자체를 변화시켰다고 해도 무방합니다.

JS 창시자인 Brendan Eich가 이 두 축을 직접 개발한 것은 아닙니다. 그렇지만 대중에게 이 개념들을 전방위적으로 접하도록 한 것은 JS였죠. 이 둘 모두 똑같이 중요하지만, 많은 JS 개발자들이 이 둘 중 하나 혹은 모두를 완전히 놓치고 있습니다. JS는 개발자 스스로 갈고 닦지 않아도, 코드를 이상하게 짜도 돌아가기는 하기 때문이죠.

이건 사실 기능입니다. 사람들이 쉽게 JS를 선택하여 익히고 사용하게 만드니까요. 그렇지만 JS 개발자로서 여러분이 그런 식으로 개발하는 것은 최대 1년 미만이어야 합니다. 이 글을 읽는 여러분이 아직 그 단계에 있다면, 이제는 레벨업을 해야 할 때입니다.

여러분이 constructor를 정의하고 여기에서 상속 받는 객체를 생성한다면, JS를 제대로 배웠다고 할 수 없습니다. JS의 가장 강력한 장점을 이용하지 못하고 있으니까요.

 

거대한 쓰레기를 만들고 있지는 않나요?

“자신이 어둠 속을 걷고 있는지 모르는 자들은 절대 빛을 찾고자 하지 않을 것이다.”
~ Bruce

Constructor는 JS의 개방 폐쇄 원칙을 위반합니다. Constructor를 호출하는 모든 코드들이 오브젝트를 생성할 때 이 constructor에 묶여 버려서 높은 의존성(highly coupled)을 갖게 되기 때문입니다. 새 객체 인스턴스를 사용하는 것 대신에 객체 툴을 사용하고 싶으신가요? 객체를 재사용하고 Garbage collector가 프레임레이트를 지연시키지 않게 하시려구요? 안타깝지만 JS에서 이렇게 하시면 모든 호출자들이 망가지거나, 돌아가더라도 어디 좀 모자란 팩토리 함수로 겨우겨우 돌아갈 겁니다.

constructor 함수로 추상 개체를 리턴하게 되면, 프로토타입 링크가 끊어지고 ‘this’ 키워드와 new 객체 인스턴스 사이의 바인드가 해제됩니다. 게다가 이렇게 만들면 this를 전혀 쓸 수 없기 때문에, 원래의 팩토리 함수보다 훨씬 제한적이죠.

더욱이, strict 모드에서 돌아가지 않는 constructor는 엄청나게 위험합니다. 호출자가 ‘new’를 놓치거나 개발자가 strict mode나 ES6 클래스(휴..)를 사용하지 않으면 this에 할당되는 모든 게 글로벌 네임스페이스에 지정됩니다. 오염 수준이죠. 그나마 Strict mode가 있기 전까지는 이런 랭귀지 차원의 결함 때문에, 찾을 수도 없는 버그가 막 발생했죠.

JS에서는 팩토리 함수가 아주 간단한 constructor 함수입니다. ‘new’ 요건, 글로벌 스페이스 문제, 희한한 제약사항들(아주 짜증나는 첫 글자 대문자화 등등을 포함하죠)이, 위에서 설명한 constructor에서 빠진다는 점이 다르죠.

다시 한 번 명확히 말하면, JS에서는 constructor 함수가 필요 없습니다. 어떤 함수든 새로운 객체를 리턴할 수 있기 때문입니다. 역동적인 객체 확장, 오브젝트 리터럴, ‘object.create()’ 만으로 귀찮은 것들 없이, 필요한 모든 것을 할 수 있습니다. 게다가 this는 다른 함수에서처럼 모두 정상 동작하고요. 엄청나죠! :)

클래스 상속의 지옥불

“좀 그러는 게 현명할 텐데도, 저는 잘 비참해지지 않네요.” ~T.H. White

모든 분들이 끓는 물 속 개구리 비유를 들어보셨을 겁니다. 개구리를 끓는 물 안에 넣으면 뛰어올라 도망치겠지만, 차가운 물에 넣었다가 서서히 끓이면 위험을 감지하지 못하기 때문에 그 안에서 죽게 됩니다. 이 소주제에서는 우리가 그 개구리입니다.

constructor가 프라이팬이라고 하면, 클래스 상속은 그냥 불이 아니라 단테의 <신곡>에 등장하는 불입니다.

고릴라/바나나 문제:

“객체 지향 언어의 문제점은 이와 동반되는 암묵적 환경에 있습니다. 바나나를 달라고 했더니, 바나나를 들고 있는 고릴라와 정글이 통째로 오는 거죠.”
~ Joe Armstrong

클래스 상속은 보통, 하나의 부모에서 자식에게 상속되는데 이 때문에 이상한 분류체계를 사용하게 됩니다. 이상하다고 말하는 이유는, 모든 객체 지향 디자인 분류체계는 처음에는 맞게 시작했다가 종국에는 틀린 체계가 되기 때문입니다. Tool과 Weapon이라는 두 가지 클래스를 만들고우리가 개발을 시작했다고 해보죠. 결과는 이미 망했습니다.

높은 의존성 문제

부모-자식 간의 의존성은 객체 지향 디자인 중에 단연 최고로 강력합니다. 즉, 재사용이 되지 않고 현대적이지 못합니다. 이런 상태에서 클래스에 아주 작은 변화만 줘도, 관련되지 조차 않아야 하는 것들이 몽땅 망가집니다.

불가피한 코드 반복 문제(the Duplication by Necessity problem)

분류 체계의 문제를 해결하기 위한 방법은 시간을 되돌아 가서, 어떤 게 어떤 것으로부터 상속 받는지를 약간 바꾸어 새 클래스를 짜는 것입니다. 그렇지만 이미 너무 심하게 의존성이 높아져버려서 제대로 추출하거나 리팩터링할 수조차 없죠. 결국은 코드를 재사용하는 것이 아니라 아예 복제를 하게 될 것입니다. 여기서 DRY(Don’t Repeat Yourself) 원칙을 위배하게 되죠

결과적으로 여러분은 아주 미묘하게 다른 클래스로 가득찬 정글을 스스로 만들게 될 겁니다. 상속 레벨을 더하면서는 클래스가 점점 더 절뚝거리게 되는 기현상을 보게 됩니다. 버그를 하나 찾으면 그 장소에서만 고치는 게 아니라 모든 코드를 다 고쳐야 하는 상황이 발생하기도 하죠.

“앗, 하나 놓쳤다!” — 이 세상의 모~든 객체 지향 프로그래머.

이건 객체 지향 디자인 내에서는 불가피한 코드 반복 문제(the duplication by necessity problem)이라고 부릅니다. ES6 클래스는 이 문제들을 전혀 해결하지 못합니다. 심지어 이 문제는 더 악화됩니다. 쓸데없이 나와서, 수 없이 많은 책과 블로그 포스트에서 애초에 갱생 불가한 이 나쁜 아이디어를 재생산하기 때문이죠.

클래스는 현재 Javascript에서 가장 해로운 기능입니다. 기술 표준화에 최선을 다한 똑똑하고 성실한 모든 사람들을 존경하지만, 그들이 늘 옳은 일만 하는 것은 아닙니다. 여전히 Brendan Eich가 웹과 프로그래밍 언어와 컴퓨터 공학의 발전 전반에 엄청난 기여를 했다고 생각하기는 합니다. :)

The Fallout

이러한 문제는 어플리케이션이 확대되면서 같이 커집니다. 그리고 결국에는 어플리케이션을 처음부터 다시 써야 하는 지경에까지 이르죠. 비즈니스 차원에서 손실까지 감당해야 하는 때가 오기도 합니다.

개발 과정에서 이러한 문제는 끊임 없이, 회사를 옮겨도, 프로젝트를 시작해도 반드시 나타납니다. 우리는 실패에서 배우기는 하는 걸까요??

제가 일했던 한 회사에서는, 이 문제 때문에 코드를 다시 처음부터 쓰기 위해 릴리즈 날짜를 일년을 통째로 옮겨야 했었습니다. 저는 업데이트를 하자는 주의이지, 다시 쓰자는 입장을 좋아하지는 않습니다. 제가 컨설팅을 했던 또 다른 회사는 그 문제가 회사 전체를 크래시 내서 망하게 할 지경이었습니다.

클래스와 상속 문제는 입맛에 맞는 스타일의 문제가 아닙니다. 이런 선택이 프로덕트 자체를 망가뜨릴 수도 있습니다.

큰 회사들은 아무것도 잘못된 게 없는 양 지나가지만, 스타트업들은 그들의 프로덕트와 마켓 핏에 매달리는 동시에 이런 IT문제 위에서 비즈니스를 굴릴 비용이 없습니다.

클래스 상속을 모두 뺀 현대적인 코드에서는 이런 문제가 전혀 발생하지 않습니다.

 

JavaScript의 광명

“완벽은 더할 것이 없을 때 완성되는 게 아니라, 더 이상 뺄 것이 없을 때 완성된다.” ~ Antoine de Saint-Exupéry

오래 전에 프로토타입식 상속을 사용하기 위한 방법을 정리하기 위해서 라이브러리 관련 조사를 한 적이 있습니다. 그러다 아주 흥미로운 생각이 떠올랐죠. 팩토리 함수를 상속을 받을 부모로 삼고 같이 함수를 쓰면 어떨까? 이걸 저는 composable 팩토리 “스탬프”라고 이름 지었습니다. 관련 라이브러리는 “Stampit”이고요. 이 라이브러리는 아주 작고 간단합니다. 2013년에 O’Reilly Fluent Conferenc에서 Stampit에 대해 이야기할 기회가 있었습니다. 그리고 이에 관한 블로그 포스트도 적었죠.

스탬프를 사용해서 코딩 스타일을 바꾼 개발자들의 커뮤니티가 생겨났고, 아직 작지만 서서히 성장하고 있습니다. Stampit은 매월 수백만명의 사용자가 있는 다양한 상용 앱에서 이용되고 있습니다.

“ Stampit을 많이 사용해왔고, 프로토타입의 종류와 스탬프의 작성 방법을 분리함으로써 오는 간편함에 반했습니다. 클래스 기반의 상속 트리는 언제나 골치가 아픈데요, 특히 비즈니스 니즈가 자주 바뀌는 경우에 특히 그렇습니다. Stampit은 오버헤드 없이 인터페이스처럼 작업하고, 개별 데이터를 다양한 소스에서 상속 받을 수 있게 해줍니다. 이제 프로토타입식 상속에 익숙해서 잘 쓰고 있고, 제가 만든 객체들은 이제 형태도, 폼도 없이 물 같아요. 클래스 같은 MSG가 없죠.” ~ Justin Schroeder, Senior Developer, Wrecking Ball Media Group

Stampit 은 물론 유일한 대안은 아닙니다. Douglas Crockford는 new나 this를 전혀 쓰지 않는 대신 코드 재사용을 위한 함수적 접근을 택합니다.

그의 객체는 상태값을 갖지 않는 함수들 뭉치입니다. 혹은 메서드 없이 데이터만 들어가 있는 오브젝트입니다. 객체를 수백, 수천개 만들어서 사용하지 않는 이상, 어플리케이션이 아주 부드럽고 실시간으로 돌아갈 수 있게 해주죠. 이런 상태에서 메서드만 호출하도록 위임하면, 수동으로 관리하는 메모리를 많이 줄여줄 수 있습니다.

다른 좋은 대안에는, 상속 대신에 JavaScript 모듈을 활용하는 방법도 있고 그냥 단순하게 객체를 클론 뜨는 방법도 있습니다. (e.g. `Object.assign()`, `$.extend()`, `_.extend()`, etc…).

복사 메커니즘은 사실 프로토타입형 상속의 또 다른 형태입니다. 클론 속성의 소스는 exemplar prototype이라는 프로토타입의 한 종류입니다. concatenative inheritance라고도 불립니다.

Dogulas Crockford 처럼 this를 더 이상 사용하지 않더라도 프로토타입 방식으로 코드 처리를 할 수도 있습니다. 동적 객체 확장이라는 JavasScript의 속성 때문에 Concatenative 상속이 가능하기 때문이죠. 이 동적 객체 확장이란 객체가 최초에 생성된 후에 객체 자체에 내용을 추가할 수 있는 기능을 뜻합니다.요는 이런 기능을 대신 활용해야 하면 되기 때문에, JavaScript에서는 클래스가 필수불가결한 경우는 전혀 없다는 것입니다.

개발자들은 종종 프로그래밍 스타일이 자신을 표현하는 방법인 것처럼, 이렇게 클래스나 상속에 대해서 비판하면 방어적이어지는 경우가 있습니다. 그렇지만 그럴 필요가 전혀 없습니다. 개발자가 코딩하여 무엇을 만드느냐가 여러분을 표현하는 방식이기 때문이죠. 어떻게 구현하느냐는 전혀 중요하지 않습니다. 엄청 이상하게 구현하지 않는 이상 말이죠.

소프트웨어 개발에 있어 가장 중요한 것은 사용자들이 당신의 소프트웨어를 좋아하느냐 입니다.

“Design Patterns” book by the Gang of Four 는 이런 관점에서 아주 명작인데요, 이 책은 다음과 같은 두가지 중요한 원칙을 기반으로 쓰여졌습니다:

“구현이 아닌 인터페이스를 위한 프로그램”, “클래스 상속 보다는 객체 작성.”

자식 클래스가 부모 클래스를 수행하도록 되어 있기 때문에, 두번째 원칙은 첫번째에 기반한다고 볼 수 있으나 보다 자세히 짚어볼 필요가 있습니다.

클래스 객체 중심 디자인의 중대한 작품은 안티-클래스 상속이다.

이 책에는 constructor와 클래스 상속의 한계를 극복하기 위한 대안으로서의 객체 생성 패턴이아예 한 챕터로 담겨 있습니다. 구글에 “new가 해롭다”, “상속이 위험하다”, “super는 정말 별로다”를 한 번 쳐보세요. 수도 없이 많은 블로그 포스트와, JS가 개발되던 시절에 나온 Dr.Dobb’s Journal 같은 좋은 책들이 결과에 나올 것입니다. 이 많은 글들이 시사하는 것은 거의 똑같습니다. new라는 클래스 상속 체계와 부모-자식 의존성(e.g. super)은 재앙과 같다는 내용이죠. 심지어 JAVA의 창시자 James Gosling도 객체가 잘못되었다는 것을 인정했습니다.

Javascript 문헌이나 계속 보고 싶다고요? Douglas Crockford 가 new를 쓰지 않아도 되도록 object.create()를 새로 만들었으니 한 번 확인해보세요. Kyle Simpson (“You Don’t Know JS”저자) 는“JS Objects: Inherited a Mess.”라는 제목의 아주 흥미로운 세 편의 블로그 시리즈를 연재했는데, 이것도 참고할 만 합니다. Kyle은 프로토타입형 상속이 클래스 개념에 반대되며, 더 간단하고 잘 기능한다고 주장합니다. 심지어는 프로토타입 위임과 클래스 상속의 구분에 대해서 명확히 하기 위해 OLOO (Objects Linked to Other Objects)라는 새로운 용어도 만들었습니다.

 

좋은 코드는 심플하다

“단순함은 말할 필요 없이 명백한 것은 빼고, 의미 있는 것만 더하는 것을 의미한다. ” ~ John Maeda

JavaScript에서 constructor와 클래스 상속을 걷어내면:

  • 더 간단해집니다: 더 읽고 쓰기 쉽습니다. 잘못된 분류 체계에 대해서도 고민할 필요가 없죠.
  • 더 유연해집니다: 새 인스턴스에서 객체 풀이나 프록시로 전환하는 것도 전혀 문제되지 않습니다.
  • 훨씬 더 강력하고 표현이 풍부해집니다: 여러 소스에서 상속을 받을 수도 있고 프라이빗 상태도 받아올 수 있죠.

더 나은 선택

“어떤 기능이 종종 위험을 초래하는데 더 나은 옵션도 존재한다면, 당연히 언제나 더 나은 옵션 쪽을 선택해야 한다.” ~ Douglas Crockford

I’m not trying to take a useful tool away from you. I’m warning you that what you think is a tool is actually a foot-gun. In the case of constructors and classes, there are several better options.

유용한 도구를 여러분에게서 앗아가려는 것이 아닙니다. 단지 여러분이 도구라고 생각하는 것이 사실은 제 발 찍을 도끼라는 것을 알려드리기 위함입니다. constructor나 클래스를 사용해야 한다는 생각이 들 때, 사실은 다른 여러 좋은 옵션이 많습니다.

코드 스타일이 예술이나 패션의 경지에 오른 양, 개발자들이 어떻게 스스로를 표현할 지를 자기가 결정할 수 있어야 한다고들 합니다. 그렇지만 아주 감정적이고 비이성적인 주장이라고 생각합니다:

화가의 붓이 자아 표현의 산물이 아니듯, 당신의 코드도 그렇지 않습니다. 코드는 도구입니다. 프로그램이 그 산물이죠.

어떤 코드는 그 자체로 예술 같을 때가 있죠. 그렇지만 이게 정말 하나의 책으로 발간되지 않는 이상, 여러분의 코드는 그렇게 되지 않습니다. 반면에 사용자들이 있는 한, 코드는 블랙 박스이고 그들이 즐기는 것은 프로그램입니다.

좋은 프로그래밍 스타일이란, 우아하고 심플하고 유연한 방법과 복잡하고 이상하고 제약이 많은 방법 중에 선택해야 할 때 전자를 선택하게 합니다. 언어의 기능을 열린 마음으로 받아들이는 게 요즘 트렌드라는 것은 알고 있지만, 여전히 옳은 방법과 그렇지 않은 방법은 있습니다. 여러분은 그 중 옳은 방법을 선택하셔야 합니다.

~ Eric Elliott


 

객체지향프로그래밍의 다형성(Polymorphism)이란?

상속 후에 여러 형태의 자식 클래스 & Over Ride를 의미한다

코드 예시

  • 부모 클래스: Animal
  • Animal을 부모로 하는 자식 클래스:Cat, Dog
public class Animal {
    public void speech() {
        System.out.println("Animal speech()");
    }
}
public class Cat extends Animal {
    @Override
    public void speech() {
        System.out.println("야옹야옹");
    }

    public void cat_method() {
        System.out.println("저는 야옹이에요");
    }
}
public class Dog extends Animal {
    @Override
    public void speech() {
        System.out.println("멍멍");
    }

    public void dog_method() {
        System.out.println("난 강아지다");
    }
}

만약

최대 10개까지의 Dog 또는 Cat 클래스로 인스턴스를 만들고, 각 클래스의 메소드를 호출해보고 싶다.

Dog로만 10개를 만들 수도 있고, Cat으로만 10개를 만들 수도 있다.

Dog 타입의 배열을 10개, Cat 타입의 배열을 10개 선언해서 각자 만들어야 하나?

    // dog + cat = 10
    Dog[] dogArr = new Dog[10];
    Cat[] catArr = new Cat[10];
    dogArr[0] = new Dog();
    catArr[0] = new Cat();
    catArr[1] = new Cat();
    dogArr[1] = new Dog();

이럴 때 Dog 클래스와 Cat 클래스의 부모 클래스인 Animal을 사용할 수 있다.

Animal ani = new Dog();
  • ani는 Animal 타입으로 선언되었지만, Dog 클래스의 객체를 참조하고 있다.
  • Dog 클래스의 객체가 할당되어 있기 때문에 Dog에 정의된 메서드를 실행할 수 있어야 함
  • 그러나 ani는 Animal 타입이기 때문에, Animal 클래스에서 정의된 속성이나 메서드만 직접적으로 호출할 수 있다.
  • Dog 클래스에서 추가된 속성이나 메서드는 ani를 통해 접근할 수 없다

 

(참고) 한편, instanceof 로 인스턴스 타입을 확인할 수 있다.

    if (ani instanceof Dog) {
        System.out.println("Dog 인스턴스입니다.");
    }

 

즉, ani는 Dog 객체를 참조하고 있지만, Animal에서 상속받아 Dog에서 오버라이드한 메서드만 접근할 수 있다는 것이다.

Dog의 고유 메소드에는 어떻게 접근할 수 있을까?

 

강제 형변환

    Dog d = (Dog)ani;

Dog 객체에서 super를 참조하고 있던 포인터가 this로 포인터를 바꾼 것과 같은 결과이다.

 

 

 

정상적으로 Dog의 고유 메소드에 접근할 수 있는 것을 확인할 수 있다.

 

 

최종 코드

// 하나의 인스턴스로 관리
Animal[] animal = new Animal[10];
animal[0] = new Dog();
animal[1] = new Cat();
animal[2] = new Cat();
animal[3] = new Dog();

for (Animal a: animal) {
    if (a != null) {
        a.speech();                 // Animal으로부터 상속받은 Cat과 Dog의 오버라이드 메소드
    }
    if (a instanceof Dog) {
        Dog d = (Dog)a;             // 강제형변환 (포인터 변경)
        d.dog_method();             // Dog의 고유 메소드
    } else if (a instanceof Cat) {
        ((Cat)a).cat_method();      // 간결한 표현식
    }
}

실행결과

OOP 특징 2 - 상속성

상속(Inheritance)란?

  • 부모 클래스로부터 속성 또는 특성 (attribute, property)을 상속받는 것
    즉, variable(변수), method(함수)를 물려받는 것임
  • 다중 상속은 불가능하다. (Java의 특징, 타언어는 가능)
    예를 들어, class Child extends Human, Animal { ... 등은 불가

구문 형식 : extends

    class 부모클래스명 {
        부모변수
        부모메소드
    }

    class 자식클래스명 extends 부모클래스명 {
        ((부모변수))
        ((부모메소드))
        자식변수
        자식메소드
    }

 

코드 예시 1: 기본적인 형태

- 부모변수: number, name

- 부모메소드: parent_method()

- 자식클래스 (=Child)에서 부모의 변수와 함수를 모두 사용할 수 있음

class Parent {
    int number;
    String name;

    void parent_method() {
        System.out.println("Parent parent_method()");
    }
}

class Child extends Parent {
}
// 메인함수
Child child = new Child();

child.number = 123;
child.name = "홍길동";
child.parent_method();

실행 결과

 

(참고) 자식 클래스의 인스턴스를 생성할 때 호출되는 생성자를 확인해보자

부모 생성자가 먼저 호출된 후, 자식의 생성자가 호출되는 것을 볼 수 있다

실행 결과

오버라이드 (Override)

- 상속 받은 클래스에서 추가/보강을 기입하는 것

- super: 부모 클래스를 가리키는 주소 (pointer; 참조 또는 reference라고도 함)

 

코드 예시 2: 부모 메소드를 오버라이딩 하는 경우

실행 결과

 

코드 예시 3: 부모 메소드를 오버라이딩 했으나, 부모 메소드를 호출하는 경우

이 때 접근지정자 super를 활용할 수 있다.

 

(참고) 부모 클래스의 멤버 변수의 접근제어자가 private 혹은 protected 일 때

private

- 자식 클래스에서 접근이 불가능하다

- 외부에서 접근이 불가능하다

- setter, getter로 외부에서 접근이 가능하다

 

코드 예시

(좌) 부모 클래스 / (우) 자식 클래스
메인 함수
실행 결과

protected

- 자식 클래스에서는 접근이 가능하다

- 외부에서는 접근이 불가능하다

- setter와 getter로 외부에서 접근이 가능하다

Over Load

  • (함수의) 이름만 같고, 매개변수의 자료형이나 개수가 다른 함수 == 동명이인
  • 왜 오버로딩을 하는가? -> 기능의 세분화
  • 같은 기능을 수행하지만 입력 받는 매개변수가 달라야 할 경우

오버로딩 함수의 예시 : Integer.parseInt()

    String str = "123";
    int num10 = Integer.parseInt(str);    // 문자열을 10진수로

    str = "1010";
    num10 = Integer.parseInt(str, 2)    // 이진수를 10진수로

코드 예시 1 : 매개변수의 자료형이 다른 경우

    void method(char c) {
        System.out.println("MyClass3 method(char c)");
    }

    void method(int i) {
        System.out.println("MyClass3 method(int i)");
    }

// 호출부
    cls.method('A');
    cls.method(123);

코드 예시 2 : 매개변수의 입력 받는 순서가 다른 경우

    void method(char c, int i) {
        System.out.println("MyClass3 method(char c, int i)");
    }

    void method(int i, char c) {
        System.out.println("MyClass3 method(int i, char c)");
    }

// 호출부
    cls.method('B', 123);
    cls.method(123, 'C');

가변인수

  • 0개 이상의 매개변수를 전달 가능함
  • ... 으로 형식 처리
  • 가변 인자는 내부적으로 배열로 처리됨
    int count(int...number) {
        int count = 0;
        for (int i=0; i<number.length; i++) {
            count = count + number[i];
        }
        return count;
    }

    // 호출부
    System.out.println(cls.count(1, 2, 3));
    System.out.println(cls.count(10,9));
    System.out.println(cls.count());

 

실행결과

 

 

1. 개념적 접근

  • 절차 지향 프로그래밍: 프로그램을 일련의 절차나 함수로 구성합니다. 데이터와 기능이 분리되어 있으며, 주로 함수 호출을 통해 흐름을 제어합니다. 예를 들어, C 언어가 대표적입니다.
  • 객체 지향 프로그래밍: 데이터를 객체로 묶어 다루며, 객체는 데이터와 해당 데이터에 대한 메서드를 포함합니다. 따라서 데이터와 기능이 밀접하게 결합되어 있습니다. Java, C++, Python 등이 이에 해당합니다.

2. 모듈화

  • 절차 지향 프로그래밍: 모듈화는 함수 단위로 이루어지며, 각 함수가 독립적으로 동작합니다. 데이터는 여러 함수에 의해 접근되고 수정될 수 있습니다.
  • 객체 지향 프로그래밍: 모듈화는 객체 단위로 이루어지며, 객체는 자신의 상태(데이터)를 보호하고, 외부에서 직접 접근할 수 없도록 캡슐화합니다. 이를 통해 데이터의 무결성을 유지할 수 있습니다.

3. 상속과 다형성

  • 절차 지향 프로그래밍: 상속 개념이 없거나 제한적이며, 함수의 재사용은 주로 복사 및 수정에 의존합니다.
  • 객체 지향 프로그래밍: 상속을 통해 기존 클래스를 확장할 수 있으며, 다형성을 통해 같은 메서드가 서로 다른 형태로 동작할 수 있습니다. 이를 통해 코드의 재사용성과 유연성을 높입니다.

4. 유지보수

  • 절차 지향 프로그래밍: 프로그램이 커지면 코드의 복잡도가 증가하고, 유지보수가 어려워질 수 있습니다. 함수 간의 의존성이 높아지기 때문입니다.
  • 객체 지향 프로그래밍: 객체 단위로 나누어져 있어 유지보수가 용이하며, 새로운 기능 추가 시 기존 코드에 미치는 영향을 최소화할 수 있습니다.

Class란?

- 객체(변수)를 사용하기 위한 설계도

- 형식:

class 클래스명 {
	변수 선언부
    ...
    함수(method) 선언부	// method == 클래스에 소속되어 있는 함수
}

 

- 클래스 객체를 선언(생성):

클래스명 객체(변수) = new 클래스명(); // new == 동적 할당

// 예시 
MyClass cls = new MyClass();	// 변수 == 객체(instance, object)를 선언

MyClass[] clsArr = new MyClass[10];	// 클래스 배열 선언

 


1. 클래스 정의

- 키워드: class

- 클래스 이름은 대문자로 시작하는 것이 관례임.

public class MyClass {
    // 속성(변수)
    int number;
    String text;

    // 생성자
    public MyClass(int number, String text) {
        this.number = number;
        this.text = text;
    }

    // 메서드
    public void display() {
        System.out.println("Number: " + number + ", Text: " + text);
    }
}

 

1-2. 접근 제어자

  • public: 모든 클래스에서 접근 가능
  • private: 같은 클래스 내에서만 접근 가능
  • protected: 같은 패키지 내 또는 자식 클래스에서 접근 가능
  • default(아무 것도 기입하지 않음): 같은 패키지 내에서만 접근 가능

 

1-3. 필드와 메서드

  • 필드: 클래스의 속성으로, 객체의 상태를 나타냅니다. // 변수 선언부
  • 메서드: 클래스의 기능으로, 객체가 수행할 수 있는 작업을 정의합니다. // 메소드 선언부

 

2. 객체 생성

- 클래스를 정의한 후, 그 클래스를 기반으로 객체를 생성할 수 있음.

public class Main {
    public static void main(String[] args) {
        // MyClass 클래스의 객체 생성
        MyClass myObject = new MyClass(10, "Hello");
        
        // 메서드 호출
        myObject.display();
    }
}

 

3. 생성자

생성자는 객체가 생성될 때 호출되는 특별한 메서드입니다. 생성자는 클래스 이름과 동일하며 반환 타입이 없습니다. 기본 생성자와 매개변수가 있는 생성자를 정의할 수 있습니다.

public class MyClass {
    int number;

    // 기본 생성자
    public MyClass() {
        number = 0;
    }

    // 매개변수가 있는 생성자
    public MyClass(int number) {
        this.number = number;
    }
}

 

 

4. 상속

Java에서는 클래스가 다른 클래스를 상속받아 기능을 확장할 수 있습니다.

public class Animal {
    public void eat() {
        System.out.println("Eating...");
    }
}

public class Dog extends Animal {
    public void bark() {
        System.out.println("Barking...");
    }
}

// 사용 예
Dog myDog = new Dog();
myDog.eat(); // 상속된 메서드 호출
myDog.bark(); // Dog 클래스의 메서드 호출

 

 

5. 인터페이스

인터페이스는 클래스가 구현해야 하는 메서드의 집합을 정의합니다. 다중 상속을 지원합니다.

public interface Animal {
    void eat();
}

public class Dog implements Animal {
    public void eat() {
        System.out.println("Dog is eating.");
    }
}

 

6. 패키지

클래스를 그룹화하여 관리할 수 있도록 패키지를 사용할 수 있습니다. 패키지는 package 키워드로 정의합니다.

package mypackage;

public class MyClass {
    // ...
}

 

 

7. 예외 처리

클래스 내에서 발생할 수 있는 예외를 처리하기 위해 try-catch 문을 사용합니다.

public class MyClass {
    public void riskyMethod() {
        try {
            // 위험한 코드
        } catch (Exception e) {
            System.out.println("예외 발생: " + e.getMessage());
        }
    }
}

+ Recent posts