객체 지향 프로그램

객체지향이란, 현실 세계의 개체(Entity)를 기계의 부품처럼 하나의 객체(Object)로 만들어, 기계쩍인 부품들을 조립하여 제품을 만들듯이 소프트웨어를 개발할 때에도 객체들을 조립해서 작성할 수 있는 기법.
  • 객체지향 기법은 구조적 기법의 문제점으로 인한 소프트웨어 위기의 해결책으로 채택되어 사용되고 있다.
  • 복잡한 구조를 단계적, 계층정으로 표현하고, 멀티미디어 데이터 및 병렬 처리를 지원한다.
  • 현실 세계를 모형화하므로 사용자와 개발자가 쉽게 이해할 수 있다.

 

객체 지향 프로그램의 장점

  • 소프트웨어의 재사용 및 확장이 용이하다.
  • 리팩토링 (유지보수)에 용이하다.
  • 디버깅이 쉽다. - 객체 단위로 코드가 나뉘어 작성되기 때문
  • 업무 분담이 용이하다. - 클래스 단위로 모듈화 시켜서 개발할 수 있기 때문

 

객체 지향 프로그램의 단점

  • 처리 속도가 상대적으로 느리다.
  • 객체가 많으면 용량이 커질 수 있다.
  • 설계 시 많은 시간과 노력이 필요하다.

 

객체지향의 주요 구성 요소와 개념

1. 객체 (Entity)

객체란, 물리적으로 존재하거나 추상적으로 생각할 수 있는 것 중에서 자신의 속성을 가지고 있고 다른 것과 식별 가능한 것.
  • 객체는 속성과 동작으로 구성되어 있는데, 속성은 필드(field), 동작은 메소드(method)라고 부른다.
  • 속성(Attribute) = 필드(Field) = 데이터 = 상태 = 변수 = 상수 = 자료 구조 : 객체가 가지고 있는 정보로 속성이나 상태, 분류 등을 나타낸다.
  • 동작(Operation) = 메소드(Method) = 서비스(Service) = 연산 : 객체가 수행하는 기능으로 객체가 갖는 데이터(속성, 상태)를 처리하는 알고리즘이다.
  • 객체는 독립적으로 식별 가능한 이름을 가지고 있다.
  • 객체가 가질 수 있는 조건을 상태(State)라고 하는데, 일반적으로 상태는 시간에 따라 변한다.
  • 객체와 객체는 상호 연관성에 의한 관계가 형성된다.
  • 객체가 반응할 수 있는 메시지(Message)의 집합을 행위라고 하며, 객체는 행위의 특징을 나타낼 수 있다.
  • 객체는 일정한 기억장소를 가지고 있다.
  • 객체의 메소드는 다른 객체로부터 메시지를 받았을 때 정해진 기능을 수행한다.
객체 모델링 (Object Modeling)이란, 현실 세계의 객체를 소프트웨어 객체로 설계하는 것. 현실 세계 객체의 속성과 동작을 추려내어 소프트웨어 객체의 필드와 메소드로 정의하는 과정.

2. 클래스 (Class)

클래스란, 공통된 속성과 연산(행위)를 갖는 객체의 집합. 객체의 일반적인 타입(Type)을 의미.
  • 클래스는 각각의 객체들이 갖는 속성과 연산을 정의하고 있는 틀이다.
  • 클래스는 객체지향 프로그램에서 데이터를 추상화하는 단위이다.
  • 클래스에 속한 각각의 객체를 인스턴스(Instance)라고 하며, 클래스로부터 새로운 객체를 생성하는 것을 인스턴스화(Instantiation)라고 한다.
  • 동일 클래스에 속한 각각의 객체(인스턴스)들은 공통된 속성과 행위를 가지고 있으면서, 그 속성에 대한 정보가 서로 달라서 동일 기능을 하는 여러 가지 객체를 나타내게 된다.
  • 최상위 클래스는 상위 클래스를 갖지 않는 클래스를 의미한다.
  • 슈퍼 클래스(Super Class)는 특정 클래스의 상위(부모) 클래스이고, 서브 클래스(Sub Class)는 특정 클래스의 하위(자식) 클래스를 의미한다.

3. 캡슐화 (Encapsulation)

캡슐화란, 객체의 필드, 메소드를 하나로 묶고, 실제 구현 내용을 감추는 것. 외부 객체는 객체 내부의 구조를 알지 못하여 객체가 노출해서 제공하는 필드와 메소드만 이용할 수 있음.
  • 필드와 메소드를 캡슐화하여 보호하면, 외부의 잘못된 사용으로 인해 객체가 손상되지 않도록 막을 수 있다.
  • 캡슐화된 객체는 인터페이스를 제외한 세부 내용이 은폐(정보 은닉)되어 외부에서의 접근이 제한적이기 때문에 외부 모듈의 변경으로 인한 파급 효과가 적다.
  • 캡슐화된 객체들은 재사용이 용이하다.
  • 객체들 간의 메시지를 주고받을 때 상대 객체의 세부 내용은 알 필요가 없으므로 인터페이스가 단순해지고, 객체 간의 결합도가 낮아진다.

4. 상속 (Inheritance)

상속이란, 부모 클래스의 멤버를 자식 클래스에게 물려주는 것.
  • 상속을 이용하면 하위 클래스는 상위 클래스의 모든 속성과 연산을 자신의 클래스 내에서 다시 정의하지 않고서도 즉시 자신의 속성으로 사용할 수 있다.
  • 하위 클래스는 상위 클래스로부터 상속받은 속성과 연산 외에 새로운 속성과 연산을 첨가하여 사용할 수 있다.
  • 소프트웨어의 재사용(Reuse) - 이미 잘 개발된 클래스를 재사용하기 때문에 코드의 중복을 줄여주고 효율적이고, 개발 시간을 절약시켜준다.
  • 다중 상속(Multiple Inheritance) : 한 개의 클래스가 두 개 이상의 상위 클래스로부터 속성과 연산을 상속받는 것이다.
  • 프로그램에서는 자식이 부모를 선택한다. 자식 클래스를 선언할 때 어떤 부모 클래스를 상속받을 것인지 선택하여 extends 뒤에 부모 클래스를 기술한다.

5. 다형성

다형성이란, 하나의 객체가 여러 가지 타입을 가질 수 있는 것. 메시지에 의해 객체(클래스)가 연산을 수행하게 될 때 하나의 메시지에 대해 각각의 객체(클래스)가 가지고 있는 고유한 방법(특성)으로 응답할 수 있는 능력.
  • 객체(클래스)들은 동일한 메소드명을 사용하며 같은 의미의 응답을 한다.
  • 응용 프로그램 상에서 하나의 함수나 연산자가 두 개 이상의 서로 다른 클래스의 인스턴스들을 같은 클래스에 속한 인스턴스처럼 수행할 수 있도록 하는 것이다.
class Parent {...}
class Child extends Parent {...}

Parent parent = new Parent() (o)
Child child = new Child() (o)
Parent p = new Child() (o)
Child c = new Parent() (x)

6. 연관성

연관성이란, 두 개 이상의 객체(클래스)들이 상호 참조하는 관계.
  • is member of (연관화, Association) : 2개 이상의 객체가 상화 관련되어 있음을 의미
  • is instance of (분류화, Classfication) : 동일한 형의 특성을 갖는 객체들을 모아 구성하는 것
  • is part of (집단화, Aggregation) : 관련 있는 객체들을 묶어 하나의 상위 객체를 구성하는 것
  • is a
    • (일반화, Generalization) : 공통적인 성질들로 추상화한 상위 객체를 구성하는 것
    • (특수화/상세화, Specialization) : 상위 객체를 구체화하여 하위 객체를 구성하는 것

 

객체지향 설계 원칙

  • SRP (Single Responsiblity Principle, 단일 책임 원칙) : 객체는 단 하나의 책임만 가져야 한다는 원칙. 응집도는 높고, 결합도는 낮게 설계하는 것을 의미한다. 변경사항이 있을 때 애플리케이션 파급 효과가 적으면 SRP 원칙을 잘 따랐다고 본다.
  • OCP (Open-Closed Principle, 개방-폐쇄 원칙) : 기존의 코드를 변경하지 않고 기능을 추가할 수 있도록 설계해야 한다는 원칙. 확장에는 열려있고, 변경에는 닫혀있어야 한다는 원칙. 요구사항에 추가사항이나 변경사항이 있더라도 기존 구성요소는 변경이 일어나지 않아야 하며, 기존 구성요소를 확장하여 재사용 가능하도록 해야한다. 공통 인터페이스를 하나의 인터페이스로 묶어 캡슐화하는 방법이 태표적이다.
  • LSP (Liskov Substitution Principle, 리스코프 치환 원칙) : 자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다는 설계 원칙. 자식 클래스는 부모 클래스의 책임을 무시하거나 재정의하지 않고 확장만 수행하도록 해야한다. 똑같은 연산을 하지만 약간씩 다르다면 인터페이스 상속의 방법을 사용한다.
  • ISP (Interface Segregation Principle, 인터페이스 분리 원칙) : 자신이 사용하지 않는 인터페이스와 의존 관계를 맺거나 영향을 받지 않아야 한다는 원칙. 어떤 클래스가 다른 클래스에 종속될 때에는 최소한의 인터페이스만을 사용해야한다. 단일 책임 원칙이 객체가 갖는 하나의 책임이라면, 인터페이스 분리 원칙은 인터페이스가 갖는 하나의 책임이다.
  • DIP (Dependency Inversion Principle, 의존 역전 원칙) : 각 객체들 간의 의존 관계가 성립될 때, 추상성이 낮은 클래스보다 추상성이 높은 클래스와 의존 관계를 맺어야 한다는 원칙. 구현 클래스가 아니라 인터페이스에 의존하라는 의미이다. 일반적으로 인터페이스를 활용하면 이 원칙은 준수된다.

'프로그래밍 > JAVA' 카테고리의 다른 글

함수형 프로그래밍이란,  (0) 2022.02.15
JVM란,  (0) 2022.02.07
어노테이션  (0) 2022.01.16
Overloading vs Override  (0) 2022.01.16
JAVA란,  (0) 2022.01.10

오버로딩 (Overloading)

- 조건 : 변수의 타입, 개수, 순서 중 하나 이상이 다르게 선언되어야 한다.

 

1. 생성자 오버로딩

생성자 오버로딩 (Overloading)이란, 매개 변수를 달리하는 생성자를 여러 개 선언하는 것.

생성자 오버로딩에서 가장 중요한 것은, 매개 변수 타입, 개수, 순서가 다르게 선언되어야 한다는 것이다.

매개 변수의 타입과 개수, 선언된 순서가 똑같을 경우, 매개 변수 이름만 바꾸는 것은 생성자 오버로딩이 아니다!!

 

2. 메소드 오버로딩

메소드 오버로딩 (Overloading)이란, 클래스 내에 같은 이름의 메소드를 여러 개 선언하는 것.

메소드 오버로딩의 조건은 매개 변수의 타입, 개수, 순서 중 하나가 달라야 한다.

메소드 오버로딩이 필요한 이유는 매개값을 다양하게 받아 처리할 수 있도록 하기 위해서이다.

이는 C언어에는 없는 특징으로, C언어에서는 같은 기능인데, 매개 변수가 조금 다르다는 이유로 함수 이름을 다르게 설정해야했는데, 이를 보완할 수 있다는 것이 장점이다.

오버로딩된 메소드를 호출할 경우 JVM은 매개값의 타입을 보고 메소드를 선택한다.

생성자 오버로딩과 마찬가지로, 매개 변수의 타입과 개수, 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 메소드 오버로딩이 아니다!! 뿐만 아니라 리턴 타입만 다르고 매개 변수가 동일해도 메소드 오버로딩이 아니다. 리턴 타입은 JVM이 메소드를 선택할 때 고려하는 것이 아니다. 그래서 컴파일 오류가 발생한다.


오버라이드 (Override)

- 상속에서 나오는 개념

더보기

상속이란, 부모 클래스의 멤버를 자식 클래스에게 물려주는 것을 의미한다. 즉, 자식 클래스에서 부모 클래스의 메소드를 가져와 사용하는 것이다.

상속의 장점은, 이미 잘 개발된 클래스를 재사용해서 새로운 클래스를 만들기 때문에 코드의 중복을 줄여준다는 것이다.

상속의 제외 조건 : 부모 클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속 대상에서 제외된다. 그리고 부모 클래스와 자식 클래스가 다른 패키지에 존재한다면, default 접근 제한을 갖는 필드와 메소드도 상속 대상에서 제외된다. 

오버라이드란, 메소드 재정의. 상속된 메소드의 내용이 자식 클래스에 맞지 않을 경우, 자식 클래스에서 동일한 메소드를 재정의하는 것

- 오버라이드를 사용하는 경우 : 부모 클래스의 어떤 메소드는 자식 클래스가 사용하기에 적합하지 않을 수 있다. 이 때, 상속된 일부 메소드를 자식 클래스에서 다시 수정해서 사용하기 위해서 사용한다.

- 메소드가 오버라이딩 되었다면 부모 객체의 메소드는 숨겨지기 때문에, 자식 객체에서 메소드를 호출하면 오버라이딩된 자식 메소드가 호출된다.

- 조건 : 부모의 메소드와 동일한 시그너처 (리턴 타입, 메소드 이름, 매개 변수 리스트)를 가져야 하고, 접근 제한을 더 강하게 오버라이딩 할 수 없으며, 새로운 예외 (Exception)를 throws 할 수 없다.

'프로그래밍 > JAVA' 카테고리의 다른 글

함수형 프로그래밍이란,  (0) 2022.02.15
JVM란,  (0) 2022.02.07
어노테이션  (0) 2022.01.16
객체 지향 프로그램이란,  (0) 2022.01.16
JAVA란,  (0) 2022.01.10

Java의 특징

1. 이식성이 높은 언어

이식성이란, 서로 다른 실행 환경을 가진 시스템 간에 프로그램을 옮겨 실행할 수 있는 것.
  • 소스 파일을 다시 수정하지 않아도, 자바 실행 환경 (JRE)이 설치되어 있는 모든 운영체제에서 실행 가능하다.
  • 이식성이 좋다는 것은 어느 플랫폼에서도 독립적이다와 동일한 말이다.
  • 주로, C언어와 비교되는 특징 - C언어는 CPU에 의해 동작하는 .exe 파일 이름으로 바이너리 코드가 생성되어 플랫폼에 따라 코드를 수정해야하는 반면, Java 언어는 .class의 바이너리 코드가 생성된 다음 인터프리터가 있는 자바 가상 머신에서 실행되기 때문에 다른 플랫폼에서 수정없이 실행 가능하다.

 

2. 객체 지향 언어

객체 지향 언어란, 프로그램을 개발하는 기법으로 부품에 해당하는 객체들을 먼저 만들고, 이것들을 하나씩 조립 및 연결해서 전체 프로그램을 완성하는 기법.
절차 지향 언어란, 객체 지향 언어와 비교되는 언어로, 개체를 순차적으로 처리하여 프로그램 전체가 유기적으로 연결되는 기법. 순서가 중요한 언어.
  • 객체를 만들기 위해 설계도인 클래스를 작성해야 하고, 객체와 객체를 연결하여 목적에 맞는 프로그램을 만들어 낸다.
  • 여러 객체를 만들고, 이것들을 조립하고 연결하여 프로그램을 만들기 때문에, 부품들은 언제든지 교체가 가능하다.
  • 캡슐화, 상속, 다형성 기능을 완벽하게 지원하고 있다.
  • 코드 재사용이 가능하다.
  • 생산성 증가. 객체에 문제가 발생했을 때, 문제가 되는 부품만 수리하면 다시 정상적으로 프로그램이 작동한다. 유지 보수가 용이하다.
  • 개발자가 생각한대로 모델링이 가능하다.
  • 하지만, 개발 속도나 실행 속도면에서 절차 지향 언어보다 느린 경향이 있다.

 

3. 함수적 스타일 코딩 지원

함수적 프로그래밍이란, 거의 모든 것을 순수 함수로 나누어 문제를 해결하는 기법. 작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 함.
  • 자바는 함수적 프로그래밍을 위해 람다식 (Lambda Expressions)을 자바 8부터 지원한다.
  • 람다식을 사용하면 컬렉션의 요소를 필터링, 매핑, 집계 처리하는데 쉬워지고, 코드가 매우 간결해진다.

 

4. 메모리 자동 관리

  • 객체 생성 시 자동적으로 메모리 영역을 찾아서 할당하고, 사용이 완료되면 Garbage Collector를 실행시켜 자동적으로 사용하지 않는 객체를 제거한다.
  • 이와 반대로, C++은 메모리 객체 제거를 개발자가 직접 코드 작성한다.

 

5. 멀티 스레드 (Multi-Thread) 쉽게 구현 가능

멀티 스레드 (Multi-Thread)란, 하나의 프로세스 내에서 둘 이상의 스레드가 동시에 작업을 수행하는 것. 하나의 프로그램이 동시에 여러 가지 작업을 처리해야 할 경우와 대용량 작업을 빨리 처리하기 위해 서브 작업으로 분리해서 병렬 처리할 때 사용.
  • 자바는 스레드 생성 및 제어와 관련된 라이브러리 API를 제공하고 있기 때문에, 실행되는 운영체제에 상관없이 멀티 스레드를 쉽게 구현할 수 있다.
  • ex. Thread Class, Runnable Interface

 

6. 동적 로딩 (Dynamic Loading) 지원

동적 로딩 (Dynamic Loading)이란, 프로그램을 실행할 때 필요할 때마다 동적으로 메모리, 객체 생성.
  • 애플리케이션이 실행될 때 모든 객체가 생성되지 않고, 객체가 필요한 시점에 클래스를 동적 로딩해서 객체를 생성한다.
  • 개발 완료 후 유지보수가 발생하더라도 해당 클래스만 수정하면 되므로 전체 애플리케이션을 다시 컴파일할 필요가 없다.
  • 유지보수를 쉽고 빠르게 진행할 수 있다.

 

7. 오픈소스 라이브러리 풍부

  • 자바는 오픈소스 언어이기 때문에 자바 프로그램에서 사용하는 라이브러리 또한 오픈소스가 넘쳐난다.
  • 검증된 오픈소스 라이브러리를 사용하면 개발 기간을 단축하면서 안전성이 높은 애플리케이션을 쉽게 개발할 수 있다.

 

 

'프로그래밍 > JAVA' 카테고리의 다른 글

함수형 프로그래밍이란,  (0) 2022.02.15
JVM란,  (0) 2022.02.07
어노테이션  (0) 2022.01.16
객체 지향 프로그램이란,  (0) 2022.01.16
Overloading vs Override  (0) 2022.01.16

이 포스트는 부스트코스 서포터즈 3기 활동을 기반으로 정리된 글입니다.

takeIf(), takeUnless()

  • takeIf() 함수 : 람다식이 true이면 객체 T를 반환하고 그렇지 않은 경우 null 반환
  • takeUnless() 함수 : 람다식이 false이면 객체 T를 반환하고 그렇지 않은 경우 null 반환
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null

!=, ==, ?. 등으로 true, false를 체크하는 것을 takeIf(), takeUnless() 함수를 사용하여 간단하게 식을 표현할 수 있음

  • takeIf(), takeUnless() + 엘비스 연산자(?:) : null일 경우 엘비스 연산자 오른쪽 식을 표현함으로써 null이 반환되었음을 표현할 수 있음

 

시간의 측정 : kotlin.system 패키지에 있는 두 개의 측정 함수 measureTimeMillis(), measureNanoTime()

  • 선언부는 다음과 같이 이루어져 있다.
public inline fun measureTimeMillies(block: () -> Unit): Long {
    val start = System.currentTimeMillis()
    block()
    return System.currentTimeMillis() - start
}

public inline fun measureNanoTime(block: () -> Unit): Long {
    val start = System.nanoTime()
    block()
    return System.nanoTime() - start
}

 

시간을 측정하고자 하는 코드를 시간 측정 함수의 인자로 보낸다.

  • 시간 측정 사용 방법
val executionTime = measureTimeMillis {
    // 측정할 작업 코드
}

println("Execution Time = $executionTime ms")

 

 

난수 생성하기 : kotlin.random.Random

  • 자바의 java.util.Random을 이용할 수도 있었지만 JVM에만 특화된 난수를 생성하기 때문에 코틀린에서는 멀티플랫폼에서도 사용 가능한 kotlin.random.Random 제공
  • 0부터 21 사이의 난수 제공 예시
import kotlin.random.Random
...
val number = Random.nextInt(21) // 숫자는 난수 발생 범위
println(number)

 

 

이 포스트는 부스트코스 서포터즈 3기 활동을 기반으로 정리된 글입니다.

run()

  • run() 함수는 인자가 없는 익명 함수처럼 동작하는 형태와 객체에서 호출하는 형태 두 가지로 사용
public inline fun <R> run(block: () -> R): R = return block()
public inline fun <T, R> T.run(block: T.() -> R): R = return block()

 

run() 실습

apply와 run을 비교해보면, apply는 "success"가 사용되지 않지만, run은 apply처럼 this로 여러 변수에 접근 가능하면서도 "success"를 반환하기까지 한다.

run은 apply의 장점을 가져와서 this를 생략 가능하기도 하다.

 

with()

  • 인자로 받는 객체를 이어지는 block의 receiver로 전달하며 결과값을 반환
  • run()함수와 기능이 거의 동일한데, run의 경우 receiver가 없지만 with()에서는 receiver로 전달할 객체를 처리
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
  • with는 세이프 콜(?.)을 지원하지 않기 때문에 다음과 같이 let과 함께 사용, let으로 null일 경우 처리
supportActionBar?.let {
    with(it) {
        setDisplayHomeAsUpEnabled(true)
        setHomeAsUpIndicator(R.drawable.ic_clear_white)
    }
}
  • let과 with을 합치면 run으로 표현할 수 있음 => null을 처리할 때에는 run()이 더 적합함

 

with() 실습

 

use()

  • use()를 사용하면 객체를 사용한 후 close()등을 자동적으로 호출해 닫아줌
public inline fun <T: Closeable?, T> T.use(block: (T) -> R): R
public inline fun <T: AutoCloseable?, R> T.use(block: (T) -> R) : R
  • T의 제한된 자료형을 보면 Closeable?로 block은 닫힐 수 있는 개체를 지정해야 함
  • Java 7 이후는 AutoCloseable?로 사용됨
  • 파일 다룰 때 유리
public fun readFirstLine(): String {
    BufferedReader (FileReader("test.file")).use { return it.readLine() }
}

use의 구현부에는 Java처럼 try-catch-finally가 들어있다.

finally 부분에는 when이 사용되어 있다.

 

use() 실습

 

 

이 포스트는 부스트코스 서포터즈 3기 활동을 기반으로 정리된 글입니다.

코틀린 제공 표준 라이브러리 함수

  • 람다식을 사용하는 코틀린의 표준 라이브러리에서 let(), apply(), with(), also(), run() 등 여러 가지 표준 함수 제공
  • 코드를 축약시키거나 사용에 간편을 더함

 

let()

  • 함수를 호출하는 객체 T를 이어지는 block의 인자로 넘기고 block의 결과값 R을 반환
public inline fun <T, R> T.let(block: (T) -> R) : R {
    ...
    return block(this)
}
  • 매개변수 block은 T를 매개변수로 받아 R을 반환
  • let() 함수 역시 R을 반환
  • 본문의 this는 객체 T를 가리키는데 람다식 결과 부분을 그대로 반환한다는 뜻
  • 다른 메소드를 실행하거나 연산을 수행해야 하는 경우 사용

 

let 실습

checkScore 함수는 일반적인 null을 검사하는 함수이고, checkScoreLet 함수는 let을 사용해 null검사를 제거하는 함수이다.

checkScore 함수에서는 null이 아니면 score을 출력한다

checkScoreLet함수에서는 ?.을 통해 null인지 확인한다. null이면 score?.let 문장을 실행하지 않는다.

null일 경우를 처리하고 싶다면, score?.let { println("Score $it")} ?: println("NULL")

이런식으로 엘비스 연산자를 사용하면 된다.

it은 score 값을 복사하여 가지고 있다.

 

let 함수의 체이닝 (chaining)

  • 여러 메소드 혹은 함수를 연속적으로 호출하는 기법

 

let의 중첩 사용

let을 중첩 사용할 수 있다. 이 때는 it을 사용하지 않고 명시적 이름을 사용해야 한다.

반환 값은 바깥 쪽의 람다식에만 적용되기 때문에 Inner String은 반환되지 않는다.

 

also()

  • also()는 함수를 호출하는 객체 T를 이어지는 block에 전달하고 객체 T 자체를 반환
public inline fun <T, R> T.let(block: (T) -> R) : R = block(this)
public inline fun <T> T.also(block: (T) -> Unit) : T {block(this); return this}
  • also는 블록 안의 코드 수행 결과와 상관없이 T인 바로 객체 this를 반환

 

also() 실습

person이 it으로 복사되어 들어감. 그래서 skills를 사용할 수 있음.

let의 success는 마지막 문장을 반환하는 반면, also의 success는 마지막 문장을 반환하지 않고, person 본체를 반환한다.

 

apply()

  • apply()함수는 also()함수와 마찬가지로 호출하는 객체 T를 이어지는 block으로 전달하고 객체 자체인 this를 반환
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
public inline fun <T> T.also(block: (T) -> Unit): T {block(this); return this}
public inline fun <T> T.apply(block: T.() -> Unit): T {block(); return this}
  • T.()와 같은 표현에서 람다식이 확장 함수로서 처리
  • 디렉터리 생성 시 사용할 수 있음

 

apply() 실습

apply() 함수에서는 this를 생략할 수 있고, this 없이 객체의 멤버에 여러 번 접근이 가능하다.

 

cf) also는 it으로 받고 생략할 수 없는 반면, apply는 this로 받고 생략 가능하다.

 

이 포스트는 부스트코스 서포터즈 3기 활동을 기반으로 정리된 글입니다.

클로저

  • 람다식으로 표현된 내부 함수에서 외부 범위에 선언된 변수에 접근할 수 있는 개념
  • 람다식 안에 있는 외부 변수는 값을 유지하기 위해 람다가 포획한 변수
  • 기본적으로 함수 안에 정의된 변수는 로컬 변수로 스택에 저장되어 있다가 함수가 끝나면 같이 사라지지만, 클로저 개념에서 포획된 변수는 참조가 유지되어 종료되어도 사라지지 않고 접근하거나 수정할 수 있게 됨
fun main() {
    val calc = Calc()
    var result = 0
    calc.addNum(2, 3) {x, y -> result = x + y} // 클로저
    println(result) // 5 출력
}

class Calc {
    fun addNum(a: Int, b: Int, add: (Int, Int) -> Unit){
        add(a, b)
    }
}

 

 

흐름 제어문

  • return : 함수에서 결과값을 반환하거나 지정된 라벨로 이동, 반환 타입을 일치시켜야함. return 이후에 작성한 식은 실행되지 않음. Unit 타입은 return을 생략할 수 있음
  • break : for나 while의 조건식에 상관없이 반복문을 끝냄
  • continue : for나 while의 반복문의 본문을 모두 수행하지 않고 다시 조건으로 넘어감

 

예외 : 실행 도중의 잠재적인 오류까지 검사할 수 없기 때문에 정상적으로 실행이 되다가 비정상적으로 프로그램이 종료되는 경우

  • 운영체제의 문제 (잘못된 시스템 호출의 문제)
  • 입력값의 문제 (존재하지 않는 파일 혹은 숫자 입력란에 문자 입력 등)
  • 받아들일 수 없는 연산 (0으로 나누기 등)
  • 메모리의 할당 실패 및 부족
  • 컴퓨터 기계 자체의 문제 (전원 문제, 망가진 기억 장치 등)

 

예외 처리

  • try-catch : try 블록의 본문을 수행하는 도중 예외가 발생하면 catch 블록의 본문 실행
  • try-catch-finally : 예외가 발생해도 finally 블록 본문은 항상 실행, 예외가 발생해도 꼭 실행해야하는 본문을 finally 블록에 넣음
  • try {} : 예외 발생 가능성 있는 문장
  • catch (e: 예외처리 클래스명) {} : 예외를 처리하기 위한 문장
  • finally {} : 반드시 실행되어야 하는 문장
  • e.printStackTrace() : 스택의 추적
  • throw Exception() : 예외 발생시킴

 

특정 예외 처리

  • 기본 : Exception
  • 산술 연산에 대한 예외를 따로 특정해서 잡을 때 : ArithmeticException

 

람다식은 비지역 반환이 일어나기 때문에 @ 라벨을 사용할 필요가 있고, 익명 함수는 라벨을 사용하지 않아도 비지역 반환이 일어나지 않는다.

 

람다식에서 return 실습

람다식에서 return을 사용하면 비지역 반환으로 람다식 return 이후에 있는 retFunc 함수 내 모든 식은 실행되지 않는다.

 

라벨을 사용하면 라벨이 있는 블록만을 하나의 블록으로 보기 때문에 람다식 바깥에 있는 retFunc 함수 내의 식이 실행된다.

return은 하나의 함수를 빠져나가기 때문에 람다식을 사용한 함수도 함수라고 생각해서 라벨을 사용하지 않고 return문을 사용하게 되면 원하는 값이 나오지 않을 수 있다. 따라서 정확히 알고 사용할 필요가 있다.

 

이번에는 암묵적 라벨을 사용한 식이다. 람다식 표현식 블록에 직접 라벨을 쓰는 것이 아니라 람다식 함수의 명칭을 그대로 라벨처럼 사용하는 방식이다.

 

익명 함수로 return 실습

익명 함수를 사용하면 라벨이 굳이 필요 없다.

대신 익명 함수를 너무 많이 쓰면 어떤 원리로 익명 함수를 쓴 것인지 헷갈릴 수 있다는 단점이 있다.

 

break, continue 실습

 

예외 처리 실습

c는 0으로 나누었기 때문에 오류가 발생한다. 그래서 After을 출력하지 못하고 catch-finally문을 실행한다.

 

예외 발생시키기

throw Exception을 사용하여 amount가 1000 이하가 되었을 때 예외가 발생하도록 하였다.

 

+ Recent posts