KotlinInAction 46

널 가능성(2)

!!(널 아님 단언) 널 아님 단언은 코틀린에서 널이 될 수 있는 타입의 값을 다룰 때 사용할 수 있는 도구 중 가장 단순하면서 무딘 도구다 !! 연산자를 사용하면 어떤 값이든 널이 될 수 없는 타입으로 바꿀 수 있다. fun main(args: Array) { ignoreNulls(null) } fun ignoreNulls(s: String?) { val sNotNull: String = s!! //예외는 이 지점을 가리킨다 println(sNotNull.length) } s 가 널이면 코틀린이 택할 수 있는 대안은 예외를 던지는 일 외에는 없다. 예외가 발생한 곳은 null값을 사용하는 코드가 아니라 단언문이 위치한 곳이다(!!) 근본적으로 !!는 컴파일러에게 이 값이 null이 아님을 알고 있음을 ..

널 가능성(1)

널 가능성은 NullPointException 오류를 피할 수 있게 돕기 위한 코틀린 타입 시스템의 특성이다. 코틀린을 비롯한 최신 언어에서 null에 대한 접근 방법은 이 문제를 실행 시점에서 컴파일 시점으로 옮기는 것이다. 날이 될 수 있는지 여부를 타입 시스템에 추가함으로써 컴파일러가 여러 가지 오류를 컴파일 시 미리 감지해서 실행 시점에 발생할 수 있는 예외의 가능성을 줄일 수 있다. 널이 될 수 있는 타입 코틀린과 자바의 첫 번째 차이이자 가장 중요한 차이는 코틀린 타입 시스템이 널이 될 수 있는 타입을 명시적으로 지원한다는 점이다. 널이 될 수 있는 타입은 프로그램 안의 프로퍼티나 변수에 null을 허용하게 하는 것인데, 어떤 변수가 널이 될 수 있다면 그 변수에 대해 메서드를 호출하게 된다면..

수신 객체 지정 람다 (with와 apply)

with 함수 수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메서드를 호출할 수 있게 하는 람다를 수신 객체 지정 람다라 부르는데, with는 수신 객체 지정 람다를 활용한다. 어떤 객체의 이름을 반복하지 않고도 그 객체에 대해 다양한 연산을 수행할 수 있다(코틀린에서는 with라는 라이브러리 함수를 제공한다) fun main(args: Array) { println(alphabet()) } fun alphabet(): String{ val result = StringBuilder() for (letter in 'A'..'Z'){ result.append(letter) } result.append("\nNow I know the alphabet!") return result.toString(..

자바 함수형 인터페이스 활용

코틀린 람다를 자바 API에서 활용할 수 있는지 살펴보자 자바 8 이전의 자바에서는 무명 클래스의 인스턴스를 만들어 메서드에 전달해 주어야 했다 추상 메서드가 단 하나만 존재하는 인터페이스를 함수형 인터페이스, SAM 인터페이스(단일 추상 메서드)라고 부른다. 코틀린은 함수형 인터페이스를 인자로 취하는 자바 메서드를 호출할 때 람다를 넘길 수 있게 해 준다(무명 클래스 인스턴스를 정의하고 활용할 필요가 없어졌다) 자바 메서드에 람다를 인자로 전달 함수형 인터페이스를 인자로 원하는 자바 메서드에 코틀린 람다를 전달할 수 있다. fun postponeComputation(delay: Int, computation: Runnable){ computation.run() } fun main(args: Array)..

지연 계산 컬렉션 연산

map, filter와 같은 함수가 반환하는 컬렉션은 즉시 생성된다. 이는 컬렉션 함수를 연쇄하면 매 단계마다 계산 중간 결과를 새로운 컬랙션에 임시로 담는다는 의미인데, 시퀀스를 사용하면 중간 임시 컬렉션을 사용하지 않고도 컬렉션 연산을 연쇄할 수 있다. fun main(args: Array) { val people = listOf( Person("Alice", 31), Person("Bob", 29), Person("Carol", 31)) println(people.map(Person::name).filter { it.startsWith("A") }) } filter, map 이 리스트를 반환하므로, 연쇄 호출은 리스트 2개를 만든다는 의미이다. 원본 리스트에 원소가 많이 생길수록 효율이 떨어진다. ..

컬렉션 함수형 API

필수적인 함수: filter와 map filter와 map은 컬렉션을 활용할 때 기반이 되는 함수이다. 대부분의 컬렉션 연산을 이 두 함수를 표현할 수 있다. filter fun main(args: Array) { val list = listOf(1, 2, 3, 4) println(list.filter { it % 2 == 0 }) //짝수만 남는다 } fun main(args: Array) { val people = listOf(Person("Alice", 29), Person("Bob", 31)) println(people.filter { it.age > 30 }) //30살 이상인 사람만 필요 } 컬렉션을 이터레이션 하면서 주어진 람다에 각 원소를 넘겨 람다가 true를 반환하는 원소만 모은다. 결..

람다 식과 멤버 참조

람다 소개 ( 코드 블록을 함수 인자로 넘기기) "이벤트가 발생하면 이 핸들러를 실행하자", "데이너 구조의 모든 원소에 이 연산을 적용하자"와 같은 생각을 코드로 표현하기 위해 일련의 동작을 변수에 저장하거나 다른 함수에 넘겨야 하는 경우가 자주 있다. 이전 자바에서는 무명 내부 클래스를 통해 목적을 달성했으며, 코드를 함수에 넘기거나 변수에 저장할 수 있는 장점이 있지만 번거롭다. 함수형 프로그래밍에서는 함수를 값처럼 다루는 접근 방법을 선택함으로써 이 문제를 해결했다. 함수형 언어에서는 하수를 직접 다른 함수에 전달할 수 있으며, 람다 식을 사용하면 코드가 더 간결해진다.(함수를 선언할 필요 없고 코드 블록을 직접 함수 인자로 전달) JAVA button.setOnClickListener(new O..

Object 키워드 : 클래스 선언과 인스턴스 생성

코틀린에서는 Object키워드를 다양한 상황에서 사용하지만 모든 경우 클래스를 정의하면서 동시에 인스턴스를 생성한다는 공통점이 있다 object 키워드를 사용하는 상황 객체 선언은 싱글턴을 정의하는 방법 중 하나 동반 객체는 인스턴스 메서드는 아니지만 어떤 클래스와 관련 있는 메서드와 펙토리 메서드를 담을 때 쓰인다. 동반 객체 메서드에 접근할 때는 동반 객체가 포함된 클래스의 이름을 사용할 수 있다. 객체 식은 자바의 무명 내부 클래스 대신 쓰인다. 싱글턴을 쉽게 만들기 자바에서는 싱글톤을 만들기 위해 보통 클래스의 생성자를 private로 제한하고 정적인 필드에 그 클래스의 유일한 객체를 저장하는 방식으로 구현한다. 코틀린에서는 객체 선언 기능을 통해 싱글톤을 언어에서 기본 지원한다. -> 객체 선언..

컴파일러가 생성한 메서드: 데이터 클래스와 클래스 위임

코틀린 컴파일러는 equals, hashCode, toString과 같은 기계적인 메서드를 생성하는 작업을 보이지 않는 곳에서 해준다. 모든 클래스가 정의해야 하는 메서드 자바와 마찬가지로 코틀린 클래스도 toString, equals, hashCode 등을 오버라이드 할 수 있다. class Client(val name: String, val postalCode:Int) 문자열 표현 : toString() class Client(val name: String, val postalCode:Int){ override fun toString(): String = "Client(name=$name, postalCode=$postalCode)" } fun main(args: Array) { val client ..

뻔하지 않은 생성자와 프로퍼티를 갖는 클래스 선언

코틀린은 주 생성자(클래스를 초기화할 때 사용, 클래스 본문 밖에서 정의)와 부 생성자(클래스 본문 안에서 정의)를 구분한다 또한 코틀린에서는 초가화 블록을 통해 초기화 로직을 추가할 수 있다. 주 생성자와 초기화 블록 보통 클래스의 모든 선언은 중괄호({}) 사이에 들어간다. 클래스 이름 뒤에 오는 괄호로 둘러싸인 코드를 주 생성자라고 부른다 주 생성자는 생성자 파라미터를 지정하고 그 생성자 파라미터에 의해 초기화되는 프로퍼티를 정의하는 목적에 쓰인다. class User constructor(_nickname: String) { val nickname: String init { nickname = _nickname } } constructor 키워드는 주 생성자나 부 생성자 정의를 시작할 때 사용한다..