5.1 필터링과 슬라이싱

Predicate

distinct

limit

skip

 

5.2 매핑

transform

flatmap

 

5.3. 검색과 매칭

allMatch

nonMatch

findAny

findFirst

 

5.4 리뷰싱

결과 도출

합, 최대/최소, 연산

어플리케이션 : 컬랙션을 만들고 처리하는 과정이 대부분

프로그래밍 : 할당, 분기, 루프

 

4.1 스트림이란 무엇인가?

투명하게, (blackbox, transparent) 병렬로 처리

선언형으로 코드 구현

뭔하는 동작을 선언 : 구현은 내부로직

 

4.2 스트림 시작하기

연속된 요소, 소스로부터 데이터 소비, 데이터 처리 연산 ( filter, map, reduce, match, sort) 파이프라이닝 ( 메소드 체이닝)

 

4.3 스트림과 컬렉션

컬렉션 : 메모리에 데이터 저장

스트림 : laziness로 필요시 연산

스트림은 한번만 탐색 가능

 

4.4 스트림 연산

시작 : stream화

중간 : filter, map, limit, loop fusion (서로다른 연산이지만 하나의 연산으로 합치는 것), short curcuit ( A && B : A가 false이면 B 수행 안함)

최종 : for each, count, collect 

 

https://steadyzest.tistory.com/14

 

쇼트서킷(Short-circuit)

관계 연산자 A == B : A와 B가 같으면 참(1) A != B : A와 B가 다르면 참(1) 논리 연산자 A && B : A와 B 모두 참일 경우 참(1). 논리곱 A || B : A와 B 둘 중 하나가 참이면 참(1). 논리합 !A : A의 결과를 참..

steadyzest.tistory.com

 

https://www.geeksforgeeks.org/short-circuit-logical-operators-in-java-with-examples/

 

Short Circuit Logical Operators in Java with Examples - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

https://core.ac.uk/download/pdf/190372509.pdf

 

3.1 람다란 무엇인가?

 

람다 lambda라는 용어는 람다 미적분학 학계에서 개발한 시스템에서 유래

람다 표현식을 사용하면 코드를 더 쉽게 구현, 간결한 표현으로 구현

람다 : 

(파라미터 리스트) -> (람다 바디)

(parameters) -> expression

(parameters) -> {statements;}

 

3.2 어디에, 어떻게 람다를 사용할까?

3.2.1 함수형 인터페이스

@FunctionalInterface

interface에 하나의 추상 메서드만 있음

전체 표현식을 함수형 인터페이스의 인스턴스

 

 

3.2.2. 함수 디스트립터

추상 메서드 시그너쳐는 람다 표현식의 시그너쳐를 가리킨다.

람다 표현식의 시그너쳐를 서술하는 메서드를 함수 디스크립터

() -> void ( runnable 같은거)

 

3.3 람다활용

 

3.4 함수형 인터페이스 사용

3.4.1 Predicate : () -> boolean

3.4.2 Consumer : (T) -> void

3.4.3 Function: (T) -> (R)

 

제공되는 함수형 인터페이스

함수형 인터페이스 함수 디시크립터 예제
Predicate<T> T -> boolean (List<String> list) -> list.isEmpty()
Consumer<T> T -> void (Apple a) -> System.out.println(a.getWeight)
Function<T> T -> R (String s) -> s.length()
Supplier<T> () -> T () -> new Apple(10)
UnaryOperator<T> T -> T (Integer a) -> a + 1
BiOperator<T> (T, T) -> T  
BiPredicate<L, R> (L, R) -> boolean  
BiConsumer<T, U> (T, U) -> void  
BiFunction<T, U, R> (T, U) -> R (Apple a1, Apple a2) ->
a.getWeight().compareTo(a2.getWeight())

 

CHAPTER 2 동작 파라미터화 코드 전달하기

 

동작 파라미터화 : 어떻게 실행할지 결정하지 않은 코드 블록

코드 블록은 나중에 프로그램에서 호출시 전달

동작이 파라미터화 : 로직, 동작을 파라미터로 전달할 수 있다.

어떤 요소에 어떤 동작을 수행하라고 전달할 수 있음

어떤 동작 다음에 다른 동작을 수행할 수도 있음

어떤 동작 수행에 에러가 발생하면 정해진 다른 어떤 동작을 수행할 수 있음


2.1 변화하는 요구사항에 대응하기

 

하드코딩 : 1st Approach if ("green".equals(apple.getColor()))
input값 전달  : 2nd Approach if (inputColor.equals(apple.getColor()))
조건 추가 if (inputColor.equals(apple.getColor())
if (inputWeight < apple.getWeight())
토글기능 추가 : 3rd Approach if (toggleOn == true && inputColor.equals(apple.getColor())
if (toggleOn == false && inputWeight < apple.getWeight())

요구사항이 추가될때 마다 if else로 하게 되면 조건이 복잡해지고 다양해짐 > 테스트 하기에러 여러움


2.2 동작 파라미터화

 

 

   
public interface WorkerPredicate {
  boolean test(Worker worker);
}
추상화
public class WorkerJobPermanantTypePredicate implements WorkerPredicate {
  public boolean test(Worker worker) {
    return worker.getJobType() == 100;
  }
}
구현
public class WorkerSalaryOver100Predicate implements WorkerPredicate {
  public boolean test(Worker worker) {
    return worker.getSalary() > 100;
  }
}
구현
public static List<Worker> filterWorker(List<Worker> workerList, WorkerPredicate p) {
   List<Worker> resultList = new ArrayList<>();
   for (Worker worker : workerList) {
      if (p.test(worker) {
          resultList.add(worker)
      }
   }
   return resultList;
}
적용
List<Worker> permanentWorkerList =
filterWorker(workerList, new WorkerJobPermamentTypePredicate());

List<Worker> salaryOver100WorkerList = 
flterWorker(workerList, new WokerSalaryOver100Predicate());
추상화로 필터링
4th Approach

유연한 API : 질의,동작,조건,행위,로직을 파라미터로 전달 

 


2.3 복잡한 과정 간소화

과거 이런 방법을 익명 클래스로 해결

List<Worker> permanentWorkerList = 
filterWorker(workerList, new WorkerPredicate() {
  public boolean test(Worker worker) {
     return worker.getJobType() = 100;
  }
});
5th Approach
익명 클래스

람다 표현식

List<Worker> permamentWorkerList =
filterWorker(workerList, (Worker w) -> w.getJobType() == 100);
6th Approach
Lambda expression

리스스 형식 + Generic 으로 일반화

public interface Predicate<T> {
   boolean test(T t);
}
 
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> result = new ArrayList<>();
    for (T e: list) {
       if(p.test(e) {
          result.add(e);
       }
    }
   return result;
}
 
List<Worker> permanentWokerList =
filter(workerList, (Worker w ) -> w.getJobType() == 100);
7th Approach
Generic + Lambda
List<Leave> paidLeaveList = 
filter(leaveList, (Leave l) -> l.getLeaveType() == "PAID");
 

2.4 실전 예제

workerList.sort(
(Worker w1, Woker w2) -> w1.getSalary().compareTo(w2.getSalary())
);
comparatore
Thread t = new Trhead(() -> System.out.println("Runing thread")); runnable
   


2.5 마치며

Generic + Lambda로 간결한 코드를 구현할 수 있다.

 

 

https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html

 

Predicate (Java Platform SE 8 )

Returns a composed predicate that represents a short-circuiting logical AND of this predicate and another. When evaluating the composed predicate, if this predicate is false, then the other predicate is not evaluated. Any exceptions thrown during evaluatio

docs.oracle.com

 

'독서관련 > Modern Java In Action' 카테고리의 다른 글

CHAPTER 4 : 스트림 소개  (0) 2022.06.28
CHAPTER 3 : 람다 표현식  (0) 2022.06.14
CHAPTER 1 자바 8 : New Feature  (0) 2022.06.02
java8 CH16 – CompletableFuture  (0) 2020.10.14
CH11 Optional, CH12 java time date  (0) 2020.10.13

CHAPTER 1 자바 8, 9, 10, 11 : 무슨 일이 일어나고 있는가?

 

1.1 역사의 흐름은 무엇인가?

 

Java 1 : 1997년

Java 5 : 2004년

Java 8 : 2014년

Java release and roadmap

 

자바 버전별 functionality

version feature
1, 2 Thread, lock, GC
5 Thread pool, noi, concurrency, annotation, generic, varargs
7 Diamond op, try resource, Fork/join
8 Default method, method reference,  lambda expression, stream api, completable future, new date api

 

Java 8 가장 큰 특징

스트림 api : 고수준 언어로 질의를 하면 구현은 최적읜 내부 실행

동작(로직)을 코드에 전달 : 메소드 참조와 람다 : 함수형 프로그래밍 개념 차입

인터페이스의 디폴트 메서드

 

1.2 왜 아직도 자바는 변화하는가?

 

Java가 대중적인 이유 :

다양한 library, 변화에 계속 적응하는 언어, 

 

현대 언어가 구추하고자하는 목표 

병렬성 지원

낮은 boiler plate code

http://www.yes24.com/Product/Goods/12204890

 

자바 8의 변화의 주요 부분

스트림 API : 스트림 처리 = 연속적인 흐름 = 파이프라인 기법

동작 파라메터화 : behavior parameterization : 작동 방식 혹은 로직을 파라메터 입력으로 넣고자 하는 것

병렬성 : 과거 병렬처리 = 프로그램의 동작방식을 변경해야함 : 현재 : parellel=1 > parallel=4 : 결과는 동일함을 보장 = 안전하게 실행

이를 위해서는 공유된 가변 데이터에 접근하는 것이 없어야함 = 순수, 부작용 없는, 상태 없는 ( pure, sife-effect-free, stateless)

과거 : 기존 값을 가공하는데 집중 : 값의 일관성 유지, 객체의 값은 변수 로직이 메소드

현재 : 함수형 프로그래밍, 원하는 결과값만 표현, 수행은 내부의 문제

 


1.3 자바 함수

 

함수 : f(x) = y 함수 = 값 : 자바 언어의 변화 : 메소드가 일급시민화 = 메소드를 값처럼 사용할 수 있다. 메소드를 인자로 넘길수 있다. 

기존자바로는 메소드를 값으로 인식하게 하여 인자에 넘길수가 없었음 : 이급시민

메서드를 인자로 넘기는 문법 : 메소드 레퍼런드 Class::method ex File::isHidden

람다문법 : 함수를 일급값으로 넘여주는 방법

기존에 있던 메소드 : 기명함수 (named) 익명함수 : 람다 (x -> x +1) = (addOne(x))

 

int addOne( x) {

return x + 1

}

Apple::isGreenApple

(Apple a) -> "green".equals(a.getColor())


1.4 스트림

List<Worker> workerList = getWorkerListFromApi();
Map<JobType, List<String>> workerMapByJobTypeAndSalaryByOver100 = new HashMap<>();
for (Worker worker : workerList) {
  if (worker.getSalary() > 100) {
      JobType jobtype = worker.getJobType();
      List<Worker> existWorkerListByJobType = workerMapByJobTypeAndSalayByOver100.get(jobtype);
      if (existWorkerListByJobType == null) {
          existWorkerListByJobType  = new ArrayList<>();
          wokerMapByJobTypeAndSalaryByOver100.put(jobType, existWorkerListByJobType)
      }
      existWokerListByJobType.add(worker);
   }
}
List<Worker> workerList = getWorkerListFromApi();
Map<JobType, List<String>> workerMapByJobTypeAndSalaryByOver100 =
workerList
.stream()
.filter( t -> t.getSalary() > 100)
.collect(groupBy(Worker::getJobType));

stream : list > stream으로 변환

포크 > 파이프라인 > 필터 > 파이프라인 > 조인 : 병렬

Divide and conquer : 분할 정복

전제조건 : 가변 공유가 없는 : 같은 것을 참조하는 게 없는 : 수정을 할수 없는 : immutable


1.5 디폴트 메서드와 자바 모듈

인터페이스 변경을 쉽게 : 인터페이스 변경이 어려운 이유 : 추가는 쉽다. 변경과 삭제가 어렵다. 추가는 정말 쉽나?

상위 인터페이스 추가하면, 하위 구현체에서 모든 abstract method를 구현해야한다.

default 메소드 정의 가능 ( 메서드 바디를 인터페이스에 일부로 포함 가능 )

default 메소드 > 인터페이스에 다중 상속 문제

 

1.6 함수형 프로그래밍에서 가져온 다른 유용한 아이디어

 

Optional

Pattern Matching


1.7 마치며

Java는 계속 성장하고 있다. 잘되는 이유 : 다 잘하는 건 아니지만.. 그래서 많은 사람이 사용하지 않을까.

메소드를 함수로 전달 : 메소드 레퍼런스

Steam API : 컬렉션 처리

인터페이스 디폴트 메서드

Optional 로 null 대체, 패턴 매팅

 

Programming Languaga Ranking in 2021

 

https://statisticstimes.com/tech/top-computer-languages.php

 

Top Computer Languages 2021 - StatisticsTimes.com

 

statisticstimes.com

 

https://dzone.com/articles/a-guide-to-java-versions-and-features

 

Guide to Java Versions and Features - DZone Java

In this guide. we will look at the differences between Java distributions and an overview of Java language features, including Java versions 8-13.

dzone.com

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

DSL을 이용하겨 코틀린 다운  API 설계

수신 객체 지정 람다 : 코드 블록에서 이름(변수)이 가리키는 대상을 결정

invoke 관례를 이용하여 람다와 프로퍼티 대입을 유연하게

 

 

API가 깔끔하다 > 1. 이름, 개념, 코드의 목적이 잘 드러나야함 2. 코드가 간결해야함

 

코틀린의 간결함

Java 구문 Kotlin 구문 기법
StringUtil.capitalize(s) s.capitalize() 확장 함수
1.to("one") 1 to "one" 중위 호출
set.add(2) set += 2 연산자 오버로딩
map.get("key") map["key"] get메소드에 대한 관계
file.use({ f -> f.read()}) file.use { it.read() } 람다를 괄호 밖으로 빼내는 관례
sp.append("a")
sp.append("b")
with (sb) {
   append("a")
   append("b")
}
수신 객체 지정 람다

general purpose programming language : 명령적 : 각 단계를 순서대로 정확히 기술 : C

domain specific language : 선언적 : 원하는 결과를 기술 : 세부 실행은 엔진 : SQL (데이터베이스 조작), REPL (문자열 조작) : 스스로 제공하는 기능을 제한함으로써 오히려 더 효율적으로 자신의 목표를 달성 : DSL을 범용 언어로 만든 호스트 어플리케이션과 함께 조합하기 어렵다

이러한 문제를 해결하면서 DSL의 다른 이점을 살리는 방법으로 internal DSL 개념 사용, 범용 언어로 작성된 프로그램의 일부, DSL의 핵심 장점을 유지하면서 주 언어를 특별한 방법으로 사용하는 것

 

기존 api 관점 : general purpose : command - query 방식

DSL 메소드 호출 : method call chaining, nested loop, combination

 

ex gradle

nested way: 

dependencies {

    compile("junit:junit:4.11")

    compile("com.google.inject:guice:4.1.0")

}

 

command-query way : 중복이 더 많음

project.dependencies.add("compile", "junit:junit:4.11")

project.dependencies.add("compile","com.google.inject:guice:4.1.0")

 

ex 코틀린 test

str should startWith("kot")

 

assertTrue(str.startWith("kot"))

 

 

수신 객체 지정 람다와 확장 함수 타입 : DSL에서 구조화된 api 구축

buildString, with, apply 표준 라이브러리

 

fun buildString (
     builderAction: (StringBuiler) -> Unit // 함수 타입인 파라미터 정의
): String {
     val sb = StringBuilder()
     builderAction(sb)
     return sb.toString()
}

>>> val s = buildString {
     it.append("hello, ")
     it.append("world !")
     // it은 StringBuilder 인스턴스 지정
}

it을 빼려면

람다를 수신 객체 지정 람다 (lambda with a receiver ) 로 변경
람다의 인자 중 하나에게 수신 객체라는 상태를 부여하면, 
이름과 마침표를 명시하지 않아도 그 인자를 멤버로 바로 사용할 수 있음

fun buildString (
     builderAction: StringBuiler.() -> Unit // 수신 객체가 있는 함수 타입의 파라미터 선언
): String {
     val sb = StringBuilder()
     sb.builderAction() // StrinbBuilder 인스턴스를 람다의 수시 객체로 넘긴다.
     return sb.toString()
}

>>> val s = buildString {
     this.append("hello, ") //this 키워드는 StringBuilder 자신
     append("world !") // this가 생략되도 묵시적으로 StringBuilder 인스턴스가 수신 객체로 취급
     // it은 StringBuilder 인스턴스 지정
}

 

파라미터 타입 선언시 일반 함수 타입 선언 대신 확장 함수 타입 extension function type을 사용

확장함수 타입 선언은 람다의 파라미터 목록에 있던 수신 객체 타입을 마라미터 목록을 여는 괄호 앞으로 빼면서 중간에 마침표(.)를 붙인 형태

(StringBuilder) -> Unit을

StringBuilder.() -> Unit으로 변경

앞에오는 (StringBuilder)를 수신객체 타입, 람다에 전다되는 그런 타입의 객체를 수신 객체

 

string.(Int, Int) -> Unit

수신객체타입 (파마리터 타임) 반환타입

 

val appendWeather : StringBuilder.() -> Unit = { this.append("How is weather?")}

>> val stringBuilder = StringBuilder("Hi! ")
>>> stringBuilder.appendWeather()
>>> println(stringBuilder)

Hi! How is Weather!

>>> println(buildString(appendWeather))
How is weather?


 

fun buildString(builderAction: StringBuilder.() -> Unit): String = 
StringBuilder().apply(builderAction).toString()


inline fun <T> T.apply(block: T.() -> Unit): T {
	block()
    return this
}

inline fun <T> T.with(receiver: T, block: T -> R): R = receiver.block()


>>> val map = mutableMapOf(1 to "one")
>>> map.apply { this[2] = "two" }
>>> with (map) { this[3] = "three" }
>>> print(map)

{1 = "one", 2 = "two", 3 = "three"}
fun createSimpleTable() = createHtml(). 
	table {
		tr {
    		td { + "cell"
            }
        }    
    }
}
  
  
open class Tag(val name: String) {
	private val children = mutableListOf<Tag>()
	protected fun <T: Tag> doInit(child: T, init: T.() -> Unit) {
		child.init()
		children.add(child)   
	}

	override fun toString() = "<$name>${children.joinToString("")}</$name>"
}
 
fun table(init: TABLE.() -> Unit) = TABLE().apply(init)

 
class TABLE: Tag(table") {
	fun tr(init : TR.() -> Unit) = doInit(TR(), init)
}
 
class TR: Tag("tr") {
   fun td(init: TD.() -> Unit) = doInit(TD(), init)
}
 
class TD: Tag

fun createTable() = 
	table {
    	tr {
        	td {
            }
        }
    }
    
>>> println(createTable())
<table><tr><td></td></tr></table>


fdsa

코틀린 빌더 : 추상화와 재사용 : TagConsumer

 

fun StringBuilder.dropdown(
	block: DIV.() -> Unit
) : String = div("dropdown", block)



 

 

invoke 관례

get, set

foo[bar]  foo.get(bar)로 변환

invoke는 () 괄호 사용

 

class Greeter(val greeting: String) {
	operator fun invoke(name: String) {
    	println("$greeting, $name!")
    }
}



>>> val usGreeter = Greeter("Hi")
>>> usGreeter("Colin")
Hi, Colin!


data class Issue {
	val id: String, val project: String, val type: String,
    val priority: String, val description: String
}

class ImportantIssuePredicate(val project: String) : (Issue) - Boolean {
	override fun invoke(issue: Issue): Boolean {
    	return issue.project == project && issue.isImportant()
    }
    
    private fun Issue.isImportant(): Boolean {
    	return type == "Bug" && (priority == "Major" || priority == "Critical")
    }
}


val i1 = Issue("IDEA-15111", "IDEA", "Bug", "Major", "Save settings failed")
val i2 = Issue("IDEA-12444", "Kotlin", "Feature", "Normal", "converter")


val predicate = ImportantIssuePredicate("IDEA")

for ( issue in listOf(i1, i2).filter(predicate) {
	print(issue.id)
}

 

a shoudl startWith("kot")


infix fun <T> T.should(matcher: Matcher<T>) = matcher.test(this)

"kotlin" should start with "kot"

"kotlin".shuld(start).with("kot)

object start

infix fun String.should(x: start): StartWrapper = StartWrapper(this)

class StartWrapper(val value: String) {
	infix fun with(prefix: String) = 
    	if (!value.startWith(prefix))
        	throw AssertionError("String does not start with $prefix: $value")
        else 
        	Unit
}


StartWrapper, EndWrapper, HaveWrapper


import java.time.Period
import java.time.LocalDate

val Int.days: Period
	get() = Period.ofDays(this)
    
val Period.ago: LocalDate
	get() = LocalDate.now() - this
    
val Period.fromNow: LocalDate
	get() = LocalDate.now() + this
    
>>> println(1.days.ago)
2016-08-16
>>> println(1.days.fromNow)
2016-08-18    

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH10 리플렉션  (0) 2021.02.13
CH10 어노테이션  (0) 2021.02.13
CH09 코틀린 제네릭스  (0) 2021.01.29
CH08 코틀린 고차함수  (0) 2021.01.28
CH07 코틀린 연산자 오버로딩  (0) 2021.01.27
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

리플렉션 : 실행시점에 동적으로 객체의 프로퍼티와 메소드에 접근할 수 있게 해주는 방법

 

코틀린에서의 리플렉션 이용 :

자바의 리플렉션 사용 : java.lang.referect 패키지 

코틀린의 리플렉션 사용 : kotlin.referect 패키지 

(별도 라이브러리 추가해야함 : org.jetbrains.kotlin:kotlin-reflect )

 

코틀린 리플렉션 API

KClass

KCallable

KFunctioin

KProperty

 

풀기능 사용 :  import kotlin.reflect.full.* 확장 함수 선언

 

KFunctionN 인터페이스 : synthetic compiler-generated type

함수 호출하기 위해서 KFunction1<Int, Unit> 로 invoke로 호출 가능, KFunction1은 제레릭 인터페이스의 첫 번째 타입 파라미터와 같다.

 

 

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH11 Domain Specific Language  (0) 2021.03.02
CH10 어노테이션  (0) 2021.02.13
CH09 코틀린 제네릭스  (0) 2021.01.29
CH08 코틀린 고차함수  (0) 2021.01.28
CH07 코틀린 연산자 오버로딩  (0) 2021.01.27
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

코틀린에서 어노케이션 인자 지정 문법

- @Annotation(TheClass::class)

- 어노테이션 인자로 어노테이션 넣는 경우는 @ 생략

- 배열 = arrayOf함수 사용

- 프로퍼티를 어노테이션 인자로 사용할 경우 const 붙여서 사용

 

어노테이션 대상

방법 : @get:Rule : 사용대상:어노테이션 이름

대상 : property, field, get, set, receiver, param, setparam, delegate, file

 

어노테이션 인자로 식 허용

 

어노테이션 선언 : annotation class JsonExclude

 

annotation class JsonName(val name: String)

// java

public @interface JsonName {
	String value();
}

 

어노테이션 제어 : 메타어노테이션

어노테이션 클래스에 적용할 어노테이션

 

@Retention(AnnotationRetention.RUNTIME) : default 값

 

inteface Company {
	val name: String
}

data class CompanyImpl(override val name: String): Company

data class Person(
  val name: String, 
  @DeserializedIntefface(CompanyImpl::class) val company: Company
)

annotaion class DeserializedInterface(val targetClass: KClass<out Any>)


annotation class CustomerSerializer {
	val serializerClass: KClass<out ValueSerializer<*>>
}


 

어노테이션 파라메터로 제네릭 클래스 받기

 

example from : github.com/yole/jkid/blob/master/src/test/kotlin/examples/_6DateSerializerExample.kt

package ru.yole.jkid.examples.customSerializerTest

import org.junit.Test
import ru.yole.jkid.CustomSerializer
import ru.yole.jkid.ValueSerializer
import ru.yole.jkid.examples.jsonSerializerTest.testJsonSerializer
import java.text.SimpleDateFormat
import java.util.*

object DateSerializer : ValueSerializer<Date> {
    private val dateFormat = SimpleDateFormat("dd-mm-yyyy")

    override fun toJsonValue(value: Date): Any? =
            dateFormat.format(value)

    override fun fromJsonValue(jsonValue: Any?): Date =
            dateFormat.parse(jsonValue as String)
}

data class Person(
        val name: String,
        @CustomSerializer(DateSerializer::class) val birthDate: Date
)

class DateSerializerTest {
    @Test fun test() = testJsonSerializer(
            value = Person("Alice", SimpleDateFormat("dd-mm-yyyy").parse("13-02-1987")),
            json = """{"birthDate": "13-02-1987", "name": "Alice"}"""
    )
}

 

책 사이트

www.manning.com/books/kotlin-in-action

kotlin-in-action 전체 예제 코드

www.manning.com/downloads/1271

 

 

json 변환 kotlin 기반 라이브러리

github.com/yole/jkid

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH11 Domain Specific Language  (0) 2021.03.02
CH10 리플렉션  (0) 2021.02.13
CH09 코틀린 제네릭스  (0) 2021.01.29
CH08 코틀린 고차함수  (0) 2021.01.28
CH07 코틀린 연산자 오버로딩  (0) 2021.01.27
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

기저 타입 : base type : LIst<String>에서 List를 base type 이라 부르고, String 은 타입 파라메터라고 부름.

LSP : Liskov Substitution Principle : A가 B의 상위 타입이라면, A 타임의 인스턴스에 대해 성립하는 모든 규칙은 B타입의 인스턴스에 대해서도 성립해야한다.

 

declaration-site variance : 선언 지점 변성

use-site variance : 사용 지점 변성

 

코틀린은 타입인자도 추론 가능, 코틀린은 타입인자가 없는 제네릭 지원안함. (자바는 하위호환성때문에 List aaa = .. 이 가능 )

제네릭 타입 인자는 컴파일 시점에만 존재

 

코틀린 Mutable<out T>는 자바 MutableList<? extends T> 와 같고

코틀린 Mutable<in T>는 자바 MutableList<? super T>와 같다.

PECS ( Procuder Extends Consumer Super)

 

MutableList<*>는 MutableList<Any?> 와 같지 않다. (MutableList<T>가 T에 대해서 무공변성)

 

자바 Function <? super T, ? extends R> 처럼 와일드 카드 사용\\

코틀린 

interface Function1<in P, out R> {

    operator fun invoke(p: P) : R

}

(P) -> R : Function1<P, R>

첫번째 타입 파라미터, 반공변, 두번째 타입 파라미터, 공변적

 

covariance invariance contravariance
공변 무공변 반공변
하위타입관계 허용 타입인자로 서로 다른 타입이 들어가면 인스턴스 타입사dl의 하위관계가 허용 안함 타입 인자의 하위 타입 관계가 뒤집힌다.
out   in
Procuder<Cat>은 Producer<Animal>의 하위 타입   Cunsumer<Animal>은 Consumer<Cat>의 하위타입
interface Producer<out T> {
    fun produce() : T
}
   
object Validators {

	private val validators = mutableMapOf(KClass<*>, FieldValidator<*>>()
    
    fun <T: Any> registerValidator (
    	kClass: KClass<T>, fieldValidator: FieldValidator<T>) {
        validators[kClass] = fieldValidator
    }
    
    @Supress("UNCHCKED_CAST")
    operator fun <T: Any> get<kClass: KClass<T>): FieldValidator<T> = 
    	validators[kClass] as? FieldValidator<T>
        	?: throws IllegalArgumentException ("No validator for ${kClass.simpleName}")
            
 
}




 

 

스타 프로젝션: 타입 대신 * 사용

제네릭 클래스의 타입 인자가 어떤 타입인지 정보가 없거나 타입 인자가 어떤 타입인지가 중요하지 않을 때 사용

 

제네릭스와 변성, 공변은 자바에서 가장 어려운 부분중 하나이니 조급하게 생각하지 말고 완전히 이해가기 보다는 익숙해지도록 해라.

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH10 리플렉션  (0) 2021.02.13
CH10 어노테이션  (0) 2021.02.13
CH08 코틀린 고차함수  (0) 2021.01.28
CH07 코틀린 연산자 오버로딩  (0) 2021.01.27
CH06 코틀린 컬렉션 및 배열  (0) 2021.01.17
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

고차 함수 : high order function : 다른 함수를 인자로 받거나 함수를 반환하는 함수 예 list.filter(x > 0)

고차 함수 정의 : 함수 타입 : 함수 문법 : (Int, String) -> Unit : 파라미터 타입 -> 반환타입

val sum : (Int, Int) -> Int = { x, y -> x + y }

val action: () -> Unit = { println(42) }

// 반환 타입 null 될 수 있음
val canReturnNull: (Int, Int) -> Int? = { x, y => null }

// 널이 될 수 있는 함수 타입 변수 정의
var funOrNull: ((Int, Int) -> Int)? = null


 

인자로 받은 함수 호출

fun String.filter(predicate: (Char) -> Boolean): String

fun String.filter(predicate: (Char) -> Boolean) : String {
	val sb = StringBuilder()
    // length = String 내 length
    for (index in 0 until length) {
    	val element = get(index)
        if (predicate(element) sb.append(element)
    }
    return sb.toString()
}

>>> println("ab1c".filter{ it in 'a'..'z'}
abc

fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = ""
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}

fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = "",
    tranform: (T) -> String = { it.toString() }
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        result.append(transform(element))
    }
    result.append(postfix)
    return result.toString()
}

>>> val letters = listOf("Alpha", "Beta")

>>> println(letters.joinToString())
Alpha, Beta

>>> println(letters.joinToString { it.toLowerCase() } )
alpha, beta

>>> println(letters.joinToString(seperator = "! ", postfix = "!", transform = { it.toUpperCase() }))
ALPHA! BETA!


fun <T> Collection<T>.joinToString (
	seperator: String = "",
    prefix: String = "",
    postfix: Sring = "",
    tranform: ((T) -> String)? = null
) : String {
	val result = StringBuilder(prefix)
    for ((index, element) in this.withIndex()) {
    	if (index > 0) result.append(prefix)
        val str = transform?.invoke()
        	?: element.toString()
        result.append(str)
    }
    result.append(postfix)
    return result.toString()
}

 

함수에서 함수 반환

enum class Delivery { STANDARD, EXPRESS }

class Order(val itemCount: Int)

fun getShippingCostCalculator(delivery: Delivery): (Order) -> Double {
	if (delivery == Delivery.EXPRESS) {
    	return { order -> 2.0 * order.itemCount }
    }
    return { order -> 1.5 * order.itemCount }
}

data class Person{
	val firtName: String,
    val lastName: String,
    val phoneNumber: String?
}

class ContactListFilters {
	var prefix: String = ""
    var onlyWithPhoneNumber: Boolean = false
    fun getPredicate(): (Person) -> Boolean {
    	val startWithPrefix = { p: Person -> 
        	p.firstName.startWith(prefix) || p.lastName.startWith(prefix)
        }
        if (!onlyWithPhoneNumber) {
        	return startWithPrefix
        }
        return { startWithPrefix(it) && it.phoneNumber != null}
    }
}

data class SiteVisit(
	val path: String,
    val duration: Double,
    val os: OS
)


enum class OS { WINDOW, LINUX, MAC, IOS, ANDROID }

val log = listOf(
	SiteVisit("/", 34.0, OS.WINDOWS),
    SiteVisit("/", 22.0, OS.MAC),
    SiteVisit("/login", 12.0, OS.WINDOWS),
    SiteVisit("/signup", 8.0, OS.IOS),
    SiteVisit("/", 16.3, OS.ANDROID),
)

fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) = 
	filter(predicate).map(SiteVisit::duration).average()
    
>>> println(log.averageDurationFor { it.os == OS.IOS && it.path = "/signup" })

 

함수 inline, noline

use  함수

withLock함수

non-local변환 : 자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만드는 return문 non-local : 인라인 함수인 경우

return이 바깥쪽 함수를 반환시킨다

인라인 되지 않는 함수에 전달되는 람다안에서 return을 사용할 수 없다.

로컬 return : 람다의 실행을 끝내고 람다를 호출했던 코드이 실행을 계속 이어간다.

return으로 실행을 끝내고 싶은 람다 식 앞에 레이블 lable@ return뒤에 그 레이블 추가

람다식에는 레이블이 2개 이상 붙을 수 없다.

무명함수는 : 코드 블록을 함수에 넘길 때 사용할 수 있는 다른 방법이다.

fun lookForAlice(people: List<Persion>) {
				   // returned
	people.forEach(fun(person) {
    	if (person.name == "Alice") return
    }
}

//returned
fun lookForAlice(people: List<Person>) {
	people.forEach {
    	if (it.name == "Alice") return
    }
}

 

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH10 어노테이션  (0) 2021.02.13
CH09 코틀린 제네릭스  (0) 2021.01.29
CH07 코틀린 연산자 오버로딩  (0) 2021.01.27
CH06 코틀린 컬렉션 및 배열  (0) 2021.01.17
CH06 코틀린 primative 타입  (0) 2021.01.11
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

자바 : 언어 기능을 타입에 의존하는 관례

코틀린 : 함수 이름에 의존하는 관례

 

자바 : + :  primative type에서 사용, String : String concat 연산

코틀린 : + : plus란 이름으로 된 연산자 + 로 대체됨, operator 키워드 붙이면 됨, 확장함수로도 정의 가능

*  관례로 쓰는 이름을 붙였는데, operator 연산자가 없으면 operator modifier is required 라고 에러

a + b - > a.plus(b) 가 됨

교환 법칙이 성립이 안됨 예를 들어 p * 1.5와 1.5 * p는 다름, p.times가 있냐와 Double.times가 있냐의 차이

 

오버로딩 가능한 이항 산술 연산자

expression operator convention
a * b times
a / b div
a % b mod (1.1 rem)
a + b plus
a - b minus
data class Point(val x: Int, val y: Int) {
	operator fun plus(other: Point): Point {
    	return Point(x + other.x, y + other.y)
    }
}

>>> val p1 = Point(1, 1)
>>> val p2 = Point(2, 3)
>>> println(p1 + p2)
Point(x = 3, y = 4)

operator fun Point.plus(other: Point): Point {
	return Point(x + other.x, y + other.y)
}

 

비트연산자 

operator convention 정의 자바
shl 왼쪽 시프트 <<
shr 오른쪽 시프트 >>
ushr 오른쪽 시프트 0 부호 비트 >>>
and &
or |
xor 배타 ^
inv 반전 ~

복합대입연산자

expression operator convention
+= plusAssign
-= minusAssign
*= timesAssign
/= remAssign

단항연산자오버로딩

expression operator convention
+a unaryPlus
-a unaryMinus
!a not
++a, a++ inc
--a, a-- dec
operator fun BigDecimal.inc() = this + BigDecimal.ONE

>>> val num = BigDecimal.ZERO

>>> println(num++)
0

>>> println(num)
1

>>> println(++num)
2

 

비교연산자

expression java
== equals
=== ==
!=  !equals
< p1.compareTo(p2) < 0
> p1.compareTo(p2) > 0
<= p1.compareTo(p2) <= 0
>= p1.compareTo(p2) >= 0

Comparable에 compareTo가 operator가 붙어 있어서 하위에서는 붙일 필요가 없다.

Comparable 인터페이스를 구현하는 모든 자바 클래스는 코틀린에서 간결한 연산자 구문으로  비교 가능

 

컬렉션

expression operator convention
val x = map[0] get
map[1] = 42 set
in contains
... rangeTo
for (x in list) { ... } iterator
val now = LocalDate.now()

val vacation = now...now.plusDays(10)

>>> println(now.plusWeeks(1) in vacation)
true



operator fun CharSequence.iterator(): CharInterator: Interator 
>> for(c in "abc") {}

operator fun ClosedRange<LocalDate>.iterator(): Interator<LocalDate> = 
	object : Iterator<LocalDate> {
    	val current = start
        override fun hasNext() = current <= endInclusive
        override fun next() = current.apply {
        	currnet = plusDays(1)
        }
    }
    
>>> val newYear = LocalDate.ofYearDay(2017, 1)
>>> val daysOff = newYear.minusDays(1)..newYear
>>> for (dayOff in dayOff) { println(dayOff) }
2016-12-31
2017-01-01

    

 

 구조분해선언 : componentN 함수

val (a, b) = p > val a = p.component1() val b = p.component2()

* data 클래스는 componentN  함수를 자동으로 만들어 준다. 코틀린 표준 라이브러리는 맨앞의 5개 원소에 대한 componentN을 제공

 

data class NameComponents(val name: String, val extention: String)

fun splitNameExt(filename: String): NameCompoents {
	val result = filename.split('.', limit = 2)
    return NameComponents(result[0], result[1])
}

>>> val (name, ext) = splitNameExt("file.txt")
>> println(name)
file

>>> println(ext)
txt

fun splitNameExt2(filename: String): Namecomponents {
	var (name, extention) = file.split('.', limit = 2)
    return NameComponents(name, extention)
}


for (entry in map.entries) {
	val key = entry.component1()
    val value = entry.compoent2()
}

 

위임프로퍼티 : 연산 위임

class Delegate {
	operator fun getValue(...) { ... }
    operator fun setValue(..., value: Type) { ... }
}

class Foo {
	var p: Type by Delegate()
}

>>> var foo = Foo()
>>> var oldValue = foo.p // delete.getValue 호출
>>> foo.p = newValue // delete.setValue 호출


backing 프로퍼티 : lazy지원, lazy다음에 로딩할 인자는 람다

class Person(val name: String) {
	val email by lazy { loadEmails(this) }
}

 

위임프로퍼티 실제 구현 : 연산 수행 값 get, set 만으로도 특정 메소드 get set이 되도록 위임가능하고 위임안에서 다른 작업도 수행 가능

open class PropertyChangeAware {
	protected val changeSupport = PropertyChangeSupport(this)
    
    fun addPropertyChangeListner(listener: PropertyChangeListenser) {
    	changeSupport.addPropertyChangeListener(listener)
    }
    
    fun removePropertyChangeListener(listener: PropertyChangeListener) {
    	changeSupport.removePropertyChangeLisner(listener)
    }
}

class ObservableProperty 
	(var propValue: Int, val changeSupport: PropertyChangeSupport) {
    
    operator fun getValue(p: Person, prop: KProperty<*>): Int = propVAlue
    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
    	val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}

class Person
	(val name: String, age: Int salary: Int) {
} : PropertyChangeAware() {

	private val observer = {
    	prop: KProperty<*>, oldValue: Int, newValue: Int -> 
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }

	var age: Int by ObservableProperty(age, changeSupport)
    var salary: Int by ObservableProperty(salary, changeSupport)
}

val x = c.prop 
// val x = <delegate>.getValue(c, <property>)
c.prop = x
// <delegate>setValue(c, <property>, x)


'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH09 코틀린 제네릭스  (0) 2021.01.29
CH08 코틀린 고차함수  (0) 2021.01.28
CH06 코틀린 컬렉션 및 배열  (0) 2021.01.17
CH06 코틀린 primative 타입  (0) 2021.01.11
CH06 코틀린 타입 시스템  (0) 2021.01.10
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

널 가능성과 컬렉션

List<Int?> : 리스트 자체는 널이 아님, Int는 널일 수 있음. 

List<Int>? : 리스트 자체는 널일 수 있음. Int는 널 아님

List<Int?>? : 리스트 자체가 널일 수 있고, Int도 널 일수 있음

List<Int> :  리스트도 널 아니고, Int도 널 아님.

 

읽기 전용과 변경 가능한 컬렉션

kotlin.collections.Collection : 컬렉션에 연산 수행하지만 읽기 전용이고, 수정을 가 할 수 없음

kotlin.collections.MutableCollection: 컬렉션에 수정 (원소 추가, 삭제 등) 가능

 

코틀린 컬렉션 : 읽기전용, 변경 가능 

읽기전용 인터페이스 : Interable

변경가능 인터페이스 : MutableIterable

kotlin

 

Java

컬렉션 생성 함수 

type Read Only Mutable
List listOf mutableListOf, arrayListOf
Set setOf mutableSetOf, hashSetOf, 
linkedSetOf, sortedSetOf
Map mapOf mutableMapOf, hashMapOf,
linkedMapOf, sortedMapOf

 

java와 혼용할때 생기는 문제

kotlin에서는 listOf생성은 readonly라고 하였는데 변경되어 있음을 아래의 코드로 증명

// java code

public class CollectionUtils {
	public static List<String> uppercaseAll(List<String> items) {
    	for (int i = 0; i < items.size(); i++){
        	items.set(i, items.get(i).toUpperCase());
        }
    }
}


// kotlin code
fun printlnUppercase(list: List<String) {
	println(CollectionUtils.uppercaseAll(list)
    println(list.first())
}


>>> var list = listOf("a", "b", "c")
>>> printlnUppercase(list)
[A, B, C]
A


 

코틀린에서는 readonly와 mutable의 interface가 다르므로 메소드 signature에 클래스를 명시적으로 읽기전용인지 변경가능인지

명시해야되므로, 혼용이 되지 않음.

더 어려운 경우는 자바에서 메소드에 컬렉션이 signature 로 들어간 경우 kotlin에서 해당 메소드를 오버라이드 할 경우, 

이때는 읽기전용으로 override할지, 변경가능으로 override해야할지를 판단해야함. 이건 케바케

 

코틀린 배열

코틀린 배열은 타이 파라미터를 받는 클래스

fun main(args: Array<String>) {
	for (i in args.indies) {
    	println("$i = ${args[i]}")
    }
}

 

코틀린 배열 생성

arrayOf

arrayOfNulls : 모든 원소가 null이고 인자로 넘긴 크기로 된 배열

코틀린 배열에도 확장함수 : (filter, map 등 ) 사용 가능

 

>>> val letters = Array<String>(26) { i -> ('a'+i).toString() }
>>> println(letters.joinToString(""))
abc....xyz


>>> val strings = listOf("a", "b", "c")
>>> println("%s/%s/%s".format(*strings.toTypedArray()))
a/b/c

>>> val fiveZeros = IntArray(5)
>>> val fiveZerosToo = IntArrayOf(0, 0, 0, 0, 0)

>>> val squares = IntArray(5) { i -> (i+1) * (i+1) }
>>> println(squares.joinToString())
1, 4, 9, 16, 25

fun main(args: Array<String>) {
	args.forEachIndexed { index, element -> 
    	println("$index : $element" )
    }
}

 

references

medium.com/@anhristyan/kotlin-collections-inside-part-1-2ad270586f4a

 

Kotlin Collections inside. Part 1.

Everyday during Java/Android development we use collection for different purposes: modifying data collections, displaying an UI with a list…

medium.com

www.programmersought.com/article/1599105415/

 

Collection framework in Kotlin - Programmer Sought

Collection framework in Kotlin tags: kotlin  set  Extension function  List  Map blog address:http://sguotao.top/Kotlin-2018-10-19-Kotlin collection framework.html In order to be good at it, it must be a tool. In Java, the collection framework occu

www.programmersought.com

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

 

코틀린은 자바의 primative type을 사용할 수 없다. wrapper type만 사용한다.

그렇다면 항상 객체 타입만 사용한다는 것인데 계산일 경우 비효율 아닌가 > 실행 시점에 코틀린의 타입은 가장 효율적인 방식으로 표현한다. 대부분의 Int타입은 int 타입으로 컴파일 함. 이런 컴파일이 불가능한 경우는 collection들의 generic을 사용하는 경우. primative type은 null을 가질 수 없는데, Int?이면 이건 null을 허용한다는 의미인데 이러한 경우에는 컴파일시 int로 컴파일 하지 않는다. 코틀린은 큰숫자로 자동 변환 하지 않는다 ( Int -> Long ). 명시적으로 변환해야한다. val l : Long = i.toLong()

 

java의 primative type으로 매핑될 수 있는 타입

정수 : Byte, Short, Int, Long

부동소수점 수 : Float, Double

문자 : Char

불리언 : Boolean

 

원시 타입 리터럴

L접미사가 붙은 Long 타입 리터럴 : 123L

표준 부동소수점 표기법을 사용한 Double 타입 리터럴 : 0.12, 2.0, 1.2e10, 1.2e-10

f나 F 접미사가 붙은 Float 타입 리터럴 : 123.4f, .456F, 1e3F

0x나 0X 접두사가 붙은 16진 리터럴 : 0xCAFEBABE, 0xbcdL

0b나 0B 접두사가 붙은 2진 리터럴 : 0b000000101

숫자 중간에 _를 붙일 수 있음 1_234L

 

특별한 타입 :

Any, Unit, Nothing

Any : 최상위 타입, Any는 널이 될 수 없음

Unit : void 타입, 함수형 프로그래밍에서 Unit은 단 하나의 인스턴스만 갖는 타입을 의미

Nothing : 

interface Processor<T> {
	fun process(): T
}

class NoResultProcessor : Processor<Unit> {
	override fun process() {
    }
}

fun fail(messsage: String) : Nothing {
	throw IllegalStateException(message)
}

val address = company.address ?: fail("No address")

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 

안전한 호출 : ?.

엘비스연산자 : ?:

널아님단언 : !!

수신객체전달 : let 함수

다른타입변환 : as?

널 가능 타입 : nullable type

읽기 전용 컬렉션 : 

fun strLen(s: String) = s.length // s는 널이 될수 없음.

fun strLen(s: String?) = ... // s는 널이 될 수 있음.

 

en.wikipedia.org/wiki/Data_type

 

Data type - Wikipedia

This article is about types of data. For more general types, including those of functions and modules, see type (computer science). Python 3: the standard type hierarchy In computer science and computer programming, a data type or simply type is an attribu

en.wikipedia.org

A data type constrains the values that an expression, such as a variable or a function, might take. This data type defines the operations that can be done on the data, the meaning of the data, and the way values of that type can be stored.

 

데이터 타입은 컴파일러나 인터프리터가 프로그래머에게 데이터를 어떻게 다루워야하는지를 알려주는 부가 정보를 포함한다. 데이터 타입은 수행할 수 있느 변수나 함수의 제약을 표현한다. 데이터 타입은 데이터에 수행할 수있는 명령과 데이터의 의미와 저장하는 방식에 대해서 정의한다. 

 

fun printAllCaps(s: String?) {
	val allCaps: String? = s?.toUpperCase()
    printin(allCaps)
}

class Employee(val name: String val manager: String?)

fun managerName(employee: Employee) : String? = employee.manager?.name

class Address(val streeAddress: String, val zipCode: Int, 
	val city: String, val country: String)
    
class Company(val name: Stirng, val address: Address?)

class Person(val name: String, val company: Company?)

fun Person.countyName(): String {
	val country = this.company?.address?.country
    return if (countrey != null) country else "Unknown"
}

 

 

엘비스 연산자 : ?: : null coalescing 

코들린에서는 return이나 throw 등의 연산도 식이다.

엘비스 연산자의 우향에 return throw 등의 연산을 넣을 수 있음

fun foo(s: String?) {
	val t: String = s ?: ""
}

s ?: ""

if s != null
	return s
else 
	""
    
fun strLenSafe(s: String?): Int = s?.length ?: 0

fun Person.countryName() = company?.address?.country ?: "Unknown"

 

안전한 캐스트 : as?

class Person(val firstName: String, val lastName: String) {
	override fun equals(o: Any?): Boolean {
    	val otherPerson = o as? Person ?: return false
        
        return otherPerson.firstName == firstName &&
        		otherPerson.lastName == lastName
    }
    
    override fun hashCode(): Int = 
    	firstName.hashCode() * 37 + lastName.hashCode()
}

 

널아님 체크 : !! 

 
fun ignoreNulls(s: String?) {
	val sNotNull: String = s!!
    println(sNotnull.length)
}


 

let 함수

자신의 수신 객체를 인자로 전달받은 람다에게 넘긴다

let 함수도 널이 될 수 있는 타입의 값에 대해 호출할 수 있지만 let은 this가 널인지 검사하지 않는다.

fun sendEmailTo(email: String) {...}

if (email != null) sendEmailTo(email)

fun sendEmailTo(email: String) {
	println("Sending email to $email")
}

var email: String? = "yole@example.com"
email?.let { sendEmailTo(it) }

email = null
email?.let { sendEmailTo(it) }

// 2줄
val person: Person? = getTheBestPersonInTheWorld()
if (person != null) sendEmailTo(person.email)


// 한줄로 줄일 수 있음
getTheBestPersonInTheWorld()?.let { sendEmailTo(it.email) }



 

나중에 초기화할 프로퍼티

코틀린에서 클래스 안의 널이 될 수 없는 프로퍼티는 생성자 안에서 초기화 해야함

lateinit 키워드는 나중에 초기화 가능, 나중에 초기화 하는 프로퍼티는 var로 해야함. val은 final이므로 생성자 안에서 초기화 해야함.

 

널이 될 수 있는 타입 확장

널이 될수 있는 확장 함수를 정의하면 null 값을 다루는 좋은 도구로 활용

코틀린에서는 널이 될 수 있는 타입의 확장 함수 안에는 this가 널이 될 수 있다는 점이 자바와 다르다

let 함수도 널이 될 수 있는 타입의 값에 대해 호출할 수 있지만 let은 this가 널인지 검사하지 않는다.

fun verifyUserInput(input: String) {
	if (input.isNullOrBlank()) {
    	println("Please fill in the required fields")
    }
}

fun String?.isNullOrBlank(): Boolean = this == null || this.isBlank()


val person: Person ? = ...

person.let { sendEmailTo(it) }

>> ERROR

person?.let { sendEmailTo(it) }

 

타입 파라미터 널 가능성

널 가능성과 자바

@Nullable + Type = Type? 

@NotNull  + Type = Type 

널 인식 가능한 annotation package : 

javax.annotation

android.support.annotation

org.jetbrains.annotation

이런 널 가능성 annotation 이 소스코드에 없는 경우 = 코틀린 플랫폼 타입

 

플랫폼 타입

코틀린이 널 관련 정보를 알수 없는 타입 : 그 타입은 널이 될 수 있는 타입으로 처리해도 되고 널이 될 수 없는 타입으로 처리해도 된다.

결국 알수 없으므로 에러 발생의 책임을 구현자에게 위임

코틀린 컴파일러는 public 함수의 널이 아닌 타입인 파라미터와 receiver에 대한 널 검사를 자동 추가

 

왜 플랫폼 타입을 도입했는가?

즉 > 모든 자바 타입을 널이 될 수 있는 타입으로 다루면 더 안전하지 않을까? 즉 널 체크를 하면 되지 않을까?

그럼 null이 아닌 값에도 null 체크를 하게 된다.

ArrayList<String?> 이면 이 값의 원소에 접근할 때마다 널 검사 수행 - > 안정성보다 검사 비용이 더 많이 호출됨 - > 비효율적

자바코드에서 가져온 타입만 플랫폼 타입이 됨

String! 타입은 자바에서 자바 코드에서 온 타입

 

상속에 대해서도 널 가능성

자바 클래스나 인터페이스를 코틀린에서 구현할 경우 널 가능성을 제대로 처리하는 일이 중요

구현 메소드를 다른 코틀린 코드가 호출가능하므로 코틀린 컴파일러는 널이 될 수 없는 타입에 선언한 모든 파라미터에 널 아님을 체크하는 단언문을 만들어줌

// java
interface StringProcessor {
	void process(String value)
}

// kotlin

class StringPrinter: StringProcessor {
	override fun process(value: String) {
    	println(value)
    }
}

class  NullableStringPrinter: StringProcessor {
	override fun process(value: String?) {
    	if (value != null)
        	println(value)
    }
}

인터페이스 + one method > 함수형 인터페이스 : 메소드가 하나이므로 메소드를 지정안해도 되고 그 인자 값에 람다를 바로 넘길 수 있음

 

button.setOnClickListener { 동작 }

 

// java

 

button.setOnclickListner(new OnClickListener() {

 @Override

public void onClick(View v) {

...

}

}

)

 

// 코틀린

button.setOnclickListner { view -> ...}

 

자바 메소드에 람다 인자로 전달

 

postponeComputation(int delay, Runnable computation)

 

Runnable은 함수형 인터페이스

 

postponeComutation(1000) { ... }

 

* 코틀린 1.0인라인 되지 않은 모든 람다 식은 무명 클래스로 컴파일 됨

 

postponeComputation( 1000, 
	object : Runnable {
    	override fun run() {
        	doSomething()
        }
    }
)    


postponeComputation(1000) { doSomething() }

val runnable = Runnable { doSomething() }

fun handleComputation() {
	postponeComputation(1000, runnable)
}

fun handleComputation(id: String) {
	postponecomputation(1000) { doSomething() }
}

 

SAM Single Abstract Method : SAM 생성자

람다를 함수형 인터페이스로 명시적으로 변경

 

val listener = OnClickListener { view ->
	val text = when (view.id) {
    	R.id.button1 -> "First Button"
        R.id.button2 -> "Second Button"
        else -> "Unknown"
    }
}

button1.setOnClickListener(listener)
button2.setOnClickListener(listener)

 

수신 객체 지정 람다 lambda with receiver : with, apply

with : 어떤 객체의 이름을 반복하지 않고도 그 객체에 대해 다양한 연산을 수행

with 파라미터가 2개 있는 함수, 첫번째 파라미터는 객체, 두번째 함수는 람다

with(stringBuilder, { ... })

첫번째 파라미터 객체 : 두번째 받을 람다의 수신 객체, 두번째 파라미터 : 람다

this@OuterClass.toString()

 

fun alphabet() : String {
	val result = StringBuilder()
    for (letter in 'A'...'Z') {
    	result.append(letter)
    }
    result.append("\n Jisoo know alphabet");
    return result.toString()
}

fun alphabet() : String {
	val stringbuilder = StringBuilder()
    return with(stringbuilder) {
    	for (letter in 'A'...'Z') {
        	this.append(letter)
        }
        this.append("\n Jisoo know the alphabet")
    }
    this.toString()
}

fun alphabet() = with(StringBuilder()) {
	for (letter in 'A'...'Z') {
    	append(letter)
    }
    toString()
}

apply : with와 동일한 방식인데 자신에게 전달된 객체(수신 객체)를 반환

apply는 확장함수로 정의되어 있음

apply 함수는 객체의 인스터스를 만들면서 즉시 프로퍼티 일부를 초기화 적용

java에서는 Builder객체가 이렇게 담당

 

fun alphabet() = StringBuilder().apply {
	for (letter in 'A'...'Z') {
    	append(letter)
    }
    append("\n Hi JiSoo")
}.toString()


fun alphabet() = buildString {
	for (letter in 'A'...'Z') {
    	append(letter)
    }
    append("\n Hi JiSoo")
}

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH06 코틀린 primative 타입  (0) 2021.01.11
CH06 코틀린 타입 시스템  (0) 2021.01.10
CH05 코틀린 람다 컬렉션 API  (0) 2021.01.05
CH05 코틀린 람다  (0) 2021.01.05
CH04 코틀린 data 클래스  (0) 2021.01.03
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

함수 동작 예제
filter 필터  
map 변환  
all 모든 원소 만족?  
any 만족 원소 하나라도 존재?  
count 원소 수 (숫자만 카운드 함)  
find 만족하는거 하나 findAny  
size 원소를 만들어서 최종 size  
groupBy 맵으로 변경  
flatMap 하위 리시트의 단일 리스트로 변환  
flatten 하위 리스트의 단일 리스트 그래도 반환  

 

지연 연산

sequence 사용

map > filter > : 매번 리스트가 생성되어 자원 낭비

list.asSequence().map().filter().toList()

// 원소의 sequence만 반환

 

시퀀스 만들기

 

// initial 0 incre 1
val naturalNumbers = generateSequence(0) { it + 1 }


// 0 to 100
val numbersTo100 = naturalNumbers.takeWhile( it <= 100 }


fun File.isInsideHiddenDirectory() = 
	generateSequence(this) { it.parentFile }.any { it.isHidden }
    
val file = File("/Users/.HiddenDir/a.txt")

println(file.isInsideHiddenDirectotry()}
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

람다 : 다른 함수에 넘길 수 있는 작은 코드 조각

A lambda expression is a short block of code which takes in parameters and returns a value

Lambdas are code blocks enclosed in curly braces

람다 식은 주로 컬렉션을 다룸

코틀린에는 함수 호출시 맨뒤에 있는 인자가 람다 식이라면 그 람다를 괄호 밖으로 빼낼수 있음

람다 파라미터 이름을 디폴트 이름 it

 


val sum = { x: Int, y: Int -> x + y }

println(sum(1, 2))

{ println(42) }()

run { println(42) }

val peopleList = listOf(Person("Alice", 29), Person("Bob", 31))

peopleList.maxBy(it.age)

peopleList.maxBy(Person::age)

peopleList.maxBy({p: Person -> p.age})

peopleList.maxBy() {p: Person -> p.age}

peopleList.maxBy {p: Person -> p.age}

peopleList.maxBy { p -> p.age }

val nameList = peopleList.joinToString( seperator = " ", 
	transform = { p: Person -> p.name})

peopleList.joinToString(" ") { p.Person -> p.name }


val sum = { x: Int, y: Int -> 
	println("computing sum")
    x + y
    }
    
fun printWith(messageC: Collection<String>, prefix: String) {
	messageC.forEach {
    	println(" $prefix $it ")
    }
}

fun printCount(responses: Collection<String>) {
	var clientErrors = 0
    var serverErrors = 0
    responses.forEach {
    	if (it.startWith("4")) {
        	clientErrors++
        } else if (it.startWith("5")) {
        	serverErrors++
        }
    }
    
    println("$clientErrors $serverErrors")
}

// 변경 가능한 복사
class Ref<T>(var value: T)

// val 변경 불가능
val counter = Ref(0)



// 변경 불가능한 변수지만 내부 필드 값은 변경 가능
// list 의 내부 값 변경과 동일
val inc = { counter.value++}

// var 변경 가능 
var counter = 0

var inc = { counter++ }

// var 변경 가능 함수
var counter = Ref(0) 클래스 인스턴스에 넣음.


 

멤버 참조 :  member reference

자바의 method reference

최상위 함수 참조

확장 함수 참조

생성자 참조 : constructor reference


peopleList.maxBy(Person::age)

// memer reference

fun salute() = println("salute")

run(::salute)

// constructor reference

data class Person(val name: String, val age: Int)

val createPerson = ::Person

val p = createPerson("Alice", 26)

// 확장 함수 reference

fun Person.isAdult() = age > 21

val predicate = Person::isAdult

// bound memer reference

val p = Person("Colin", 34)

val personAgeFunction = Person::age

println(personAgeFunction(p))

val personAgeFunction= p::age

println(personAgeFunction())

 

코틀린이 보통 람다를 무명 클래스로 컴파일 하지만 그렇다고 람다 식을 사용할 때 마다 새로운 클래스가 만들어지지 않는다

람다가 변수를 포획하면 생성되는 시점마다 새로운 무명 클래스가 생긴다

즉 실행 시점에 무명 클래스 생성에 따른 부가 비용이 소요

람다를 사용하는 구현은 똑같은 작업을 수행하는 일반 함수를 사용한 구현보다 덜 효율적

 

java lambda

colinkang.tistory.com/61?category=951152

 

CH03 Lambda expression, CH04 Stream

아래의 글은 라울-게이브리얼 우르마, 마리오 푸스코, 앨런 마이크로프트 저/우정은 역, 『모던 자바 인 액션』,한빛미디어(2019), CH01의 내용을 기반으로 작성하였습니다. 람다 •표현식 •(파라

colinkang.tistory.com

 

 

www.w3schools.com/java/java_lambda.asp

 

Java Lambda Expressions

Java Lambda Expressions Java Lambda Expressions Lambda Expressions were added in Java 8. A lambda expression is a short block of code which takes in parameters and returns a value. Lambda expressions are similar to methods, but they do not need a name and

www.w3schools.com

kotlinlang.org/docs/reference/lambdas.html

 

Higher-Order Functions and Lambdas - Kotlin Programming Language

 

kotlinlang.org

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석 역, 『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

클래스는 equals, hashCode, toString 메소드 구현할 필요가 있음. 데이터 클래스에 유용한 메소드 자동 생성

1. to String 오버라이딩

2. 객제 값 비교 == > java equals === > java ==

3. data class 붙이면 equals, hashCode, toString 모두 구현

4. data class 추가 메소드 : copy() 

 

class Client(val name: String, val postalCode: Int) {
	override fun toString() = "Client(name: ${name}  postalCode: ${postalCode})"
}

class Client(val name: String, val postalCode: Int) {
	override fun equals(other: Any?) : Boolean {
    	if (other == null || other !is Client)
        	return false
    	return name == other.name && postalCode == other.postalCode	
    }
    override fun hashCode() : Int {
    	return name.hashCode() * 31 + postalCode.hashCode()
    }
}


 

데코레이터 패턴으로 상속 금지를 우회하여 기능 추가

데코레이터 클래스의 만들고 내부에 필드로 해당 클래스 그리고 기존 클래스와 같은 클래스로 인터페이스 유지 되게 만들어준다.

이 접근의 단점은 준비코드가 많음.

코틀린은 이런 위임을 일급 함수로 지원

 

class DelegatingCollection<T> {
	innerList: Collection<T> = ArrayList<T>()
} : Collection<T> by innerList {}


class CountingSet<T> {
	val innerSet: MutableCollection<T> = HashSet<T>()
} : MutableCollection<T> by innerSet {
	var objectAdded = 0
    override fun add(element: T) : Boolean {
    	objectAdded++
        return innerSet.add(element)
    }
    override fun addAll(c: Collection<T>) : Boolean {
    	objectedAdded += c.size
        return innerSet.addAll(c)
    }
}

 

object 키워드 

1. object : 클래스를 정의하면서 동시에 인스턴스를 생성한다는 공통점

2. object declaration은 싱글턴 정의 방법중 하나

3. 동반 객체 : companion object : 인스턴스 메소드는 아니지만 어떤 클래스와 관련 있는 메소드와 팩토리 메소드를 담을 때 쓰인다.

4. 객체 식은 자바의 anonymous inner class 대신 사용

5. 객체 선언도 클래스나 인터페이스를 상속할 수 있다. 특정 인터페이스 구현시 그 구현 내부에 다른 상태가 필요하지 않은 경우

6. 코틀린은 자바 static 키워드 지원하지 않음.

7. 무명 객체 정의

 

object Payroll {
	val allEmployees = arrayListOf<Person>()
    
    fun calculateSalary() {
    	for (person in allEmployees) {
        }
    }
}

// 생성자는 객체 선언에 사용할 수 없다.

Payroll.allEmployees.add(Person(...))

Payroll.calculateSalary()

object CaseSensitiveFileComparator : Comparator<File> {
	override fun compare(file1: File, file2: File) : Int {
    	return file1.path.compareTo(file2.path, ignoreCase = true)
    }
}

// 중첩 객체

data class Person(val name: String) {
	object NameComparator : Comparator<Person> {
    	override fun compare(p1: Person, p2: Person) : Int = p1.name.compareTo(p2.name)
    }
}

person.sortedWith(Person.NameComparator)

// 자바에선 유일한 클래스 인스턴스 변수 

CaseInsensitiveFileComparator.INSTANCE.compare(file1, file2);

 

companion 객체

1. 클래스 밖에 있는 최상위 함수는 클래스의 private 멤버 변수를 사용할 수 없다.

2. 동반 객체는 자신을 둘서싼 클래스의 모든 private 멤버에 접근 할 수 있다.

3. 팩토리 패턴을 구현하기에 좋음.

4. 클래스를 확장해야만 하는 경우에는 동반 객체 멤버를 하위 클래스에서 오버라이드 할 수 없으므로 여러 생성자를 사용하는 편이 나음

5. 동반 객체는 클래스 안에 정의된 일반 객체다. 이름 붙이거나, 인터페이스 상속하거나, 확장 함수와 프로퍼티 정의 가능

 

class User private constructor(val nickname: String) {
	companion object {
    	fun newSubscribingUser(email: String) = User(email.substringBefore('@'))
        fun newFacebookUser(accountId: Int) = User(getFacebookName(accountId))
    }
}

User.newSubscribingUser("bob@gmail.com")
User.newFacebookUser(4)


class Person(val name: String) {
	companion object Loader {
    	fun fromJSON(jsonText: String) : Person = ...
    }
}

class Person(val firstName: String, val lastName: String) {
	companion object {
    }
}

class Person.Companion.fromJSON(json: String) : Peson {
...
}

 

object expression : 객체 식

1. 무명객체는 싱글턴이 아님

2. 객체 식은 무명 객체 안에서 여러 메소드를 오버라이드 해야 하는 경우에 훨씬 더 유명하다.

3. 코틀린의 SAM (single abstract method) 함수형 인터페이스 , 인터페이스에 메소드 하나

 

window.addMouseListener(
    // 객체 이름 이 빠짐
	object: MouseAdapter() {
    	override fun mouseClicked(e: MouseEvent) {...}
        override fun mouseEntered(e: MouseEvent) {...}
    }
)


val listner = 
object: MouseAdapter() {
	override fun mouseClicked(e: MouseEvent) {...}
	override fun mouseEntered(e: MouseEvent) {...}
}

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석 역, 『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

클래스와 인터페이스

 

1. 인터페이스에 프로퍼티 선언이 들어갈 수 있다.

2. sealed는 클래스 상속을 제한한다. ( when으로 분기 타는데 상속 받으면 else 에서 처리 될 수도 있음, 이를 막자)

3. 기본 선언은 public final 임

4. 인터페이스에 구현이 있는 메소드 (java 8의 디폴트 메서드와 비슷)

5. 클래스 이름 뒤에 : 인터페이스 혹은 클래스 ( extends와 implements가 별도 없다.)

6. override 변경자 반드시 사용

7. 디폴트 메서드 default 안 붙여도 됨

8. open final abstract 변경자 (  modifier ) 

9. 상속을 허용 하려면 open을 붙여야 한다.

10. visibility modifier : public , private, protected, internal (자바의 기본인 패키지 전용은 없음), internal : 한꺼번에 컴파일 되는 코틀린 파일

11. 최상위 선언 ( 클래스 함수 프로퍼티 )에 private 가능

12. protected 오직 상속 클래스에서만 접근 ( 자바는 같은 패키지 안에서 접근 가능)

13. 코틀린 nested class : class A, inner class : inter class A ( 자바 nested class : static class A, inner class : class A (클래스안에)

 

* fragile base class : 하위 클래스가 상속할 경우 상위 클래스의 정확한 규칙을 이해 못하고 구현 할 경우 문제가 발생할 수 있음. effective java에서는 확실하지 않으면 상속 못하게 하던지 설명을 다 넣던지. 

 

interface Clickable {
	fun click()
    fun showOff() = println("clickable.");
}

interface Focusable {
	fun setFocus(value: Boolean) = println("focus { if (value) "on" else "off"}.")
    fun showOff() = println("focusable.");
}

class Button : Clickable, Focusable {
	override fun click() = println("clicked.")
    override fun showOff() {
    	super<Clickable>.showOff()
        super<Focusable>.showOff()
    }
}

open class RichButton: Clickable {
	fun diable() {}
    open fun animate() {}
    final override fun click() {}
}

abstract class Animated {
	abstract fun animate()
}

class Outer {
	inner class Inner {
    	fun getOuterReference(): Outer = this@Outer
    }
}

sealed class Expr {
	class Num(val value: Int) : Expr()
    class Sum(val left: Expr, val right: Expr) : Expr()    
}

fun eval(e: Expr) : Int = 
	when(e) {
    	is Expr.Num -> e.value
        is Expr.Sum -> e.left + e.right
        // else 가 필요 없음 sealed 키워드 
        // 추후에 Expr에 추가되면 컴파일 오류나고 여기에도 추가해야됨. 
    }

 

생성자 관련

 

14. 기반 클래스 이름 위에는 꼭 빈 괄호가 들어간다.

15. 싱글턴 클래스 > 비공개 생성자 private constructor

16. 부 생성자가 필요한 이유는 자바 상호 운영성

 

class User(val nickname: String)

class User constructor(_nickname: String) {
	val nickname: String
    init {
    	nickname = _nickname
    }
}

class User(_nickname: String) {
	val nickname = _nickname
}

class User(val nickname: String, val isSubscribed: Boolean = true)

open class Button // 인자가 없는 디폴트 생성자가 만들어진다.

class RadioButton: Button() // Button 클래스의 생성자를 호출

class Secretive private constructor() {}


open class View {
	constructor(ctx: Context) {
    }
    
    constructor(ctx: Context, attr: AttributeSet) {
    }
}

class MyButton: View {
	constructor(ctx: Context) : super(ctx) {
    }
    
    constructor(ctx: Context, attr : AttributeSet) : super(ctx, attr) {
    }
}

class MyButton2: View {
	constructor(ctx: Context) : this(ctx, MYSTYLE) {
    }
    constructor(ctx: Context, attr : AttributeSet) : super(ctx, attr) {
    }
}

 

인터페이스에 선언된 프로퍼티

 

interface User {)
	val nickname : String
}

class PrivateUser(override val nickname: String) : User

class SubscribeUser(val email: String) : User {
	override val nickname: String 
        // 이 값 불리때 마다 실행
    	get() = email.substringBefore('@')
}

class FacebookUser(val accountId: Int) : User {
    // 방식 초기화시 한번 실행
	override val nickname = getFacebookName(accountId)
    // getFackbookName은 다른 곳에 정의 된 것으로 가정
}

 

어떤 값을 저장하되 그 값을 변경하거나 읽을 때 정해진 로직을 실행하는 프로퍼티

field라는 특별한 식별자를 통해서 뒷받침할 필드에 접근 가능

게터에서는 field 값을 읽을 수만 있고, 세터에서는 field 값을 읽고 쓰기 가능

 

class User(val name: String) {
	var address: String = "unknown"
    set(value: String) {
    	println("address changed ${field} -> ${value}")
        field = value
    }
}


class LengthCounter {
	var counter: Int = 0
    private set
    fun addWord(word: String) {
    	counter += word.length
    }
 }
 
 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH05 코틀린 람다  (0) 2021.01.05
CH04 코틀린 data 클래스  (0) 2021.01.03
CH03 함수 정의와 호출  (0) 2021.01.01
CH02 코틀린기초 - try catch finally  (0) 2021.01.01
CH02 코틀린기초 - 맵이터레이션  (0) 2021.01.01
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

1. 함수정의와 호출

2. 확장함수와 확장프로퍼티 사용하여 자바 라이브러리를 코틀린 스타일로 적용

3. 확장을 통해 자바 라이브러리를 사용

 

컬렉션 만들기

 

val list = arrayListOf(1, 7, 53)
val map = hashMapOf(1 to "one", 7 to "seven", 10 to "ten")
val set = hashSetOf(1, 7, 53)

println(list)

list.toString)

 

print(joinToString(list, ";", "(", ")")

(1; 2; 3)

 

코틀린에서는 명시 파라미터 네임, 이름 붙인 인자, named arguments, default arguments

 

jointToString(collection, separator = " ", prefix = " ", postfix = " ")

 

kotlinlang.org/docs/reference/functions.html

 

Functions: infix, vararg, tailrec - Kotlin Programming Language

 

kotlinlang.org

default arguments value

collection: Collection<T>,

separator: String = ",",

prefix: String = "", // default argument value

postfix: String = "" // default argument value

): String

 

최상위 함수와 프로퍼티 : 기존의 util 클래스를 없애준다.

자바로 컴파일된 것을 보면 컴파일한 파일 명의 class의 static으로 생성됨

@file:JvmName("StringFunctions")

 

import strings.StringFunctions;

StrinFunctions.joinToString(list, "", "", ""); 로 java에서 대응됨

 

최상위 프로퍼티 값은 정적 필드에 해당됨.

const val UNIX_LINE_SEPERATOR = "\n"

 

// java

 

public static final String UNIX_LINE_SEPERATOR = "";

 

자바 라이브러리 코드와 공존하는 코드

기존 자바 API를 재작성하지 않고 코틀린이 제공하는 여러 편리한 기능을 사용할 수 있는 방법

확장 함수 extension function : 어떤 클래스의 멤버 메소드인 것 처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수

확장 함수 만들기 : 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이면 됨

fun String.lastChar(): Char = this.get(this.length -1) // 수신 객체에 this 없이도 접근 가능하다

클래스 이름 : 수신 객체 타입 : receiver type

확장 함수가 오출 되는 대상이 되는 값(객체) : 수신 객체 : receive object

수신 객체 타입은 확장이 정의될 클래스의 타입이며, 수신 객체는 그 클래스에 속한 인스턴스 객체

>>> println("Kotlin".lastChar()) // String이 수신 객체 타입 "Kotlin"이 수신 객체

>>> n

확장 함수가 캡슐화를 깨지는 않는다. 즉 private, protected 멤버는 사용할 수 없다.

내부적으로 확장 함수는 수신 객체를 첫번째 인자로 받는 정적 메소드 임.

// java

char c = StringUtilKt.lastChar("Kotlin")

확장 함수는 단지 정적 메소드 호출에 대한 문법적인 편의 임.

확장 함수는 오버라이드 할 수 없다. : static 이기 때문에 

확장 함수를 첫 번째 인자가 수신 객체인 정적 자바 메소드로 컴파일 된다. > 오버라이드가 안된다. > static 메소드는 멤버 메소드가 아니기 때문

 

import로 사용

import strings.lastChar

import strings.*

import strings.lastChar as last

 

확장 프로퍼티 

기존 클래스 객체에 대한 프로퍼티 형식 ( get set ) 의 구문으로 사용할 수 있는 API 추가

 

val String.lastChar: Char

get() = get(length-1)

set(cvalue: Char) {

this.setChar(length-1, cvalue)

}

 

>>> println("Kotlin".lastChar)

n

>>> val sb = StringBuilder("Kotlin?")

>>> sb.lastChar = '!'

>>> println(sb)

Kotlin!

 

자바에서 확장 프로퍼티 사용 : StringUtilKt.getLastChar("Java")

 

컬렉션 처리 : 

 

1. 자바 컬렉션 API 확장 :  확장 함수

2. 가변 인자 함수 : fun listOf<T>(vararg value: T) : List<T> { . . . } // java는 타입 뒤에 ... 코틀린은 파라미터 앞에 vararg

스프레드 연산자 *args : 배열에 들어있는 원소를 가변 길이 인자로 넘길 때도 코틀린은 명시적으로 배열을 풀어서 전달함 // java는 그냥 넘기면 됨

3. 값의 쌍 다루기 : 중위 호출과 구조분해 선언

val map = mapOf( 1 to "one", 7 to "seven")

중위 호출 infix call : to라는 일반 메소드 호출

 

// 스칼라와 동일

1 to "one" 는 이렇게 내부적으로 메소드 호출로 변환됨

1.to("one")

인자가 하나 뿐인 일반 메소드나 인자나 하나 뿐인 확장 함수는 중위 호출 사용 가능

infix fun Any.to(other: Any) = Pair(this, other)

val (number, name) = 1 to "one"

이것을 구조분해 선언 destructuring declaration

fun <K, V> mapOf(vararg values: Pair<K, V>) : Map<K, V>

 

문자열과 정규식 다루기

자바 : split메소드로는 "."을 이용해 문자열을 분리할수 없음. 왜냐하면 내부적으로 정규식이고 정규식에서 "."는 모든 문자를 나타냄

코틀린에서는 split 확장 함수를 통해서 String이 아닌 Regex 타입의 값을 받는 split

println("12.345-6.A".split("\\.|-".toRegex())

[12, 345, 6, A]

3중 따옴표 문자열

1. 정규식의 어떤 문자도 이스케이프할 필요 없음.

2. 여러 줄 문자열 도 만들 수 있음

3. 3중 따옴표 문자열 안에 $ (문자열 템플릿) 사용안되기 때눙네 ${'$'}  사용

기존 라이브러리를 새 언어에 활용하는 패턴 : Pimp My Library www.artima.com/weblogs/viewpost.jsp?thread=179766

 

 

Pimp my Library

Ideas, Languages, and Programs Pimp my Library by Martin Odersky October 9, 2006 Summary What to do if you are stuck with existing libraries and API's. Advertisement There's a fundamental difference between your own code and libraries of other people: You

www.artima.com

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
	if (user.name.isEmpty())
    	throw IllegalArgumentException("Can't save : name empty : userId => ${user.id} ")
    if (user.address.isEmpty())
    	throw IllegalArgumentException(Can't save : address empty")
	// save to db
}

fun saveUser(user: User) {
	fun validate(user: User, value: String, fieldName: String) {
    	if (value.isEmpty() 
        	throw IllegalArgumentException("Can't save : ${fieldName} empty : userId => ${user.id} ")
    }
    validate(user, user.name, "name")
    validate(user, user.address, "address")

	// save to db
}

fun User.validateBeforeSave() {
	fun validate(value: String, fieldName: String) {
    	if (value.isEmpty() 
        	throw IllegalArgumentException("Can't save : ${fieldName} empty : userId => ${id} ")
    }
    validate(user.name, "name")
    validate(user.address, "address")    
}


// 더 간단해 짐
fun saveUser(user: User) {
	user.validateBeforeSave()
    // save to db
}


 

 

 

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

1. 코틀린도 자바와 다르게 체크 예외와 언체크 예외를 구별하지 않는다.

2. 잡아내도 되고 안잡아도 되고

3. try 본문은 {} 블록으로 싸야한다.

 

// java style

fun readNumber(reader: BufferedReader): Int? {
	try {
    	val line = reader.readLine()
        return Integer.praseInt(line)
    }
    catch (e: NumberFormatException) {
    	return null
    }
    finally {
    	reader.close()
    }
}

// try를 식으로 사용

fun readNumber(reader: BufferedReader): Int? {
	val number = try {
    	Integer.parseInt(reader.readLine())
    } catch (e: NumerFormatException) {
    	return
    }
    println(number)
}


// try return 생략

fun readNumber(reader: BufferedReader): Int? {
	val number = try {
    	Integer.parseInt(reader.readLine())
    } catch (e: NumberFormatException) {
    	null
    }
    println(number)
    
val reader = BufferedReader(StringReader("42"))

readNumber(reader)

>>> 42
}




 

맵 이터레이션

val binaryCharMap = TreeMap<Char, String>()

for (c in 'A'..'F') {
	val binary = Integer.toBinaryString(c.toInt())
    binaryCharMap[c] = binary
    // java style binaryCharMap.put(c, binary)
}

for ((ch, bi) in binaryCharMap) {
	println("C ${ch} : Binary ${bi}"
}

val list = arrayListOf("a", "b", "c")

for((idx, e) in list.withIndex()) {
	println("Index ${idx} Element ${e}")
}

in으로 범위 검사

 

fun isLetter(c: Char) = c in 'a'..'z' || c in 'A'..'Z'

fun isNotDigit(c: Char) = c !in '0'..'9'

fun recongnize(c:char) = 
	when (c) {
		in '0'..'9' -> "digit"
        in 'a'..'z', in 'A'..'Z' -> 'Alpabet'
        else -> "Unknon char ${c}"
	}


"Kotlin" in "Java".."Scala" 
// "Java" <= "Scala" && "Kotlin" <= "Scala"


>>> printlin("Kotlin" in "Java".."Scala")
>>> true

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH03 함수 정의와 호출  (0) 2021.01.01
CH02 코틀린기초 - try catch finally  (0) 2021.01.01
CH02 코틀린 기초 - loop  (0) 2021.01.01
CH02 코틀린 기초 - 스마트 캐스트  (0) 2020.12.30
CH02 코틀린 기초  (0) 2020.12.28
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

 

while 루프

while ( 조건 true) {

}

 

do {

} while (조건 true) 

 

val oneToTeh = 1..10 (이건 scala 에서 봤고)

 

for (i in 1..100) {

}

 

for (i in 100 downTo 1 step 2) {

}

// 이건 좀 익숙해져야하거나 다른 식으로 표현하는 방법이 있을 듯.

 

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

( 1 + 2 ) + 4 같은 산술식을 계산하는 함수

 

interface Expr

class Num(val value: Int): Expr

class Sum(val left: Num, val right: Num): Expr

// (1 + 2) + 4
Sum(Sum(Num(1), Num(4)), Num(4))

 

1. 식은 트리 구조로 저장됨, Num은 노드에서 말단, Sum 은 자식 둘 있는 중간 노드, Sum의 두 자식은 덧셈의 인자

2. Expr 인터페이스 구현

3. class 뒤의 : Expr은 인터페이스 구현 한다는 의미 ( fun의 뒤의 : returnType 은 리턴값이다. class 뒤의 인터페이스 구현은 좀 이상하지 않는가 라고 생각했는데, java에서도 a implements Expr, b implements Expr , class Sum 의 메소드는 public Expr sum(Int a, Int b) 뭐 이런깃으로 표현을 하는데 즉, interface 구현은 즉 상위 구현체의 리턴으로 upcasting 된 것을 의미하므로 return type이자 interface의 구현이기 때문이라고 표현할 수 있으니 결과론 적으로 return type을 inteface로 표현했다는 것은 inteface의 구현이라고 생각할 수 있음, 놀랍군.)

4. 스마트 캐스트는 타입 검사 뒤에 변경 될 수 없는 변수에만 적용 가능하다 ( p 148), 즉 final 이여야하는데, 프로퍼티는 기본 final 임

 

// java style

fun eval(e: Expr): Int = 
	if (e is Num) {
    	val n = e is Num
        return n.value
    }
    if (e is Sum) {
    	return eval(e.right) + eval(e.left)
    }
    throw IllegalArgumentException("Unknown expression")

fun eval(e: Expr): Int = 
	if (e is Num) {
    	e.value
    } else if (e is Sum) {
    	eval(e.right) + eval(e.left)
    } else {
    	throw IllegalArgumentException("Unknown expression")
    }

eval(sum(Num(1), Num(2)))

// use when expression

fun eval(e:Expr): Int = 
	when(e) {
    	is Num -> e.value
        is Sum -> eval(e.right) + eval(e.left)
        else -> throw IllegalArgumentException("Unknown expression")
    }


fun evalWithLogging(e: Expr): Int = 
	when(e) {
    	is Num -> {
        	println("Num: ${e.value}")
            e.value
        }
        is Sum -> {
        	val left = evalWithLogging(e.left)
            val right = evalWithLogging(e.right)
            print("sum: ${left} + ${right}")
            left + right
        }
        else -> throw IllegalArgumentException("Unknown expression")
    }
    

 

 

 

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH02 코틀린기초 - 맵이터레이션  (0) 2021.01.01
CH02 코틀린 기초 - loop  (0) 2021.01.01
CH02 코틀린 기초  (0) 2020.12.28
CH00 kotlin useful info  (0) 2020.12.27
CH01 코틀린이란 무엇이며, 왜 필요한가?  (0) 2020.12.27
아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

변수 variable

함수 function

클래스 class

프로퍼티 property

스마트 캐스트 smart cast : 타입 검사, 타입 캐스트, 타입 강제 변환

타입 추론 : type inference

문자열 템플릿 : string template : 변수를 문자열 안에서 사용

when : java의 switch : 코틀린에서는 enum class : 자바는 그냥 enum

코틀린의 if는 값을 만들어내기때문에 java의 3항 연산자 a > b ? a : b 가 if (a > b) a else b 로 됨 그래서 3항 연산자가 별도로 없다.

if 문에 식이 하나면 중괄호 생략 그리고 결과값, 하나 이상의 식이면 블럭으로 감싸고 마지막이 결과값이 여야함.

식이 본문인 함수는 블록을 본문으로 가질수 없고

* if (a > b ) a else b

블록이 본문인 함수는 내부에 return 문이 반드시 있어야 한다.

* if (a > b) {

print(echo) 

return a }

else 

b

 

함수 선언 : fun

함수를 최상위 수준에 정의할 수 있다. (자바는 클래스 안에 선언 > 자바로 변환하면 어떻게 나올라나...)

; 세미 콜론을 끝에 붙이지 않아도 된다.

 

구문 statement와 표현식 expression의 구분

statement는 if는 표현식이다.

표현식은 값을 만들어내며 ( return 값이 있다로 설명) 다른 식의 하위 요소로 계산에 참여할 수 있다. 

구문은 자신을 둘러싸고 있는 가장 안쪽 블록의 최상위 요소로 존재하여 아무런 값을 만들어내지 않는다.  (return 값이 없다.)

자바는 모든 제어 구조가 구문 ( return 이 없으므로 명시적으로 return 해줘야함)

코틀린은 루프를 제외한 대부분의 제어 구조가 식 ( return이 됨)

대입식, 비교식

 

식이 본문인 함수 : fun max( a: Int, b: Int): Int = if (a > b) a else b (if 도 식임)

반환타입 생략 (추론 가능) fun max(a: Int, b: Int) = if (a>b) a else b

 

변수 

val :변하지 않은 값

var : 변하는 값

 

val question = "질문"

 

val answer = 42

 

val ansswer2: Int = 42

 

val yearstoCompute = 7.5e6

 

val answer: Int

answer = 42

 

val 참조 자체는 불편이더라도 참조가 가리키는 객제 내부의 값은 변경 가능

val languageList = arrayListOf("Java")

languageList.add("Kotlin")

 

var answer = 42

answer = "답" // 컴파일 오류

 

문자열 템플릿

$변수

${구문}

 

Hello $name!

$name님 반가워요! // 컴파일 오류

${name}님 반가워요! // 잘됨

변수명을 감싸는 습관 

컴파일된 코드는 StringBuilder를 사용

 

클래스 

코틀린의 기본 가시성은 public 임, ( 프로퍼티에 is를 붙일지 말지가 고민이네, Boolean으로 선언했을 경우 즉 person.isMarried 가 맞는지 person.married가 맞는지 naming convention을 찾아봐야 겠다)

커스텀 getter setter도 가능 (오버라이딩인가...)

// example of value object : 값 객체

// Java version
public class Person {
	private final String name;
    public Person(String inputName) {
    	this.name = inputName
    }
    
    public String getName() {
    	return this.name;
    }
    
}

// Kotlin version

class Person(val name: String)


// 
class Person(val name: String, var isMarried: Boolean)
// name 읽기 전용, isMarried 읽기쓰기


val person = Person("Bob", true)
person.getName() // 가능
person.name // 가능 내부적으로 getName으로 변환

person.isMarried = false // setMarried(false) 동일


class Rectangle(val height: Int, val width: Int) {
	val isSquare: Boolean
	get() {
    	return height == width
    }
}



 

프로퍼티

자바에서는 필드와 접근자를 한데 묶어 프로퍼티 property 라고 함

val 프로퍼티 : 읽기 전용

var 프로퍼티 : 변경 가능

커스텀 접근자 

val isSquare: Boolean

get() {

return height == width

}

get() = height == width

set(setvalue: Boolean) {

isSquare = setvalue

}

 

import class 및 import static method 가 import로 됨

star import하면 해당 패키지 안의 클래스, 최상위 함수 프로퍼티 모드 로딩됨

 

패키지 구조와 디렉터리 구조가 맞지 않아도 된다.하지만 자바의 규칙을 따르자. 나중에 자바로 변환히 문제가 생길수도 있다.

 

enum: enum은 class 앞에 있을 때는 특별한 의미를 지니지만 다른 곳에서는 이름에 사용할 수 있다. 소프트 키워드

반면 class는 키워드다 클래스를 표현하는 변수를 사용할때는 claszz나 aClass 같은 이름을 사용해야함.

break를 넣지 않아도 됨 (그럼 반대로 2개 가 같은 갑 매핑은? 아 좋네 되네 )

 

fun getMnemonic(color: Color) = 
	when (color) {
    	Color.RED -> "Richard"
        Color.YELLOW -> "Of"
        Color.BLUE -> "Battle"
    }
    
    
 getMnemonic(Color.BLUE)
 
 
 
 fun getMnemonic(color : Color) = 
 	when(color) {
    	Color.RED, Color.ORANGE -> "warm"
        Color.GREEN, Color.BLUE -> "cold"
    }
    
    
fun mix(c1: Color, c2: Color) = 
	when(setOf(c1, c2)) {
    	setOf(RED, YELLOW) -> ORANGE
        setOf(BLUE, YELLOW) -> GREEN
        else -> throw Exception("MisMatch color")
    }
    
// 인자 없는 when

fun mixOptimized(c1: Color, c2: Color) = 
	when {
    	(c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) -> ORANGE
        (c1 == BLUE && c2 == YELLO) -> GREEN
        else -> throw Exception("Notmatched color)
    }
    
    

 

스마트 캐스트 : 타입 검사와 타입 캐스트 조합 : 중요하므로 별도 post로 split

colinkang.tistory.com/entry/CH02-%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-%EC%8A%A4%EB%A7%88%ED%8A%B8-%EC%BA%90%EC%8A%A4%ED%8A%B8

 

CH02 코틀린 기초 - 스마트 캐스트

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. ( 1 + 2 ) + 4 같은 산술식을 계산하는 함수 interface Expr

colinkang.tistory.com

Loop

colinkang.tistory.com/entry/CH02-%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B8%B0%EC%B4%88-loop

 

CH02 코틀린 기초 - loop

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. while 루프 while ( 조건 true) { } do { } while (조건 true) va..

colinkang.tistory.com

 

맵 이터레이션

colinkang.tistory.com/entry/CH02-%EC%BD%94%ED%8B%80%EB%A6%B0%EA%B8%B0%EC%B4%88-%EB%A7%B5%EC%9D%B4%ED%84%B0%EB%A0%88%EC%9D%B4%EC%85%98

 

CH02 코틀린기초 - 맵이터레이션

맵 이터레이션 val binaryCharMap = TreeMap () for (c in 'A'..'F') { val binary = Integer.toBinaryString(c.toInt()) binaryCharMap[c] = binary // java style binaryCharMap.put(c, binary) } for ((ch, bi)..

colinkang.tistory.com

try catch

colinkang.tistory.com/entry/CH02-%EC%BD%94%ED%8B%80%EB%A6%B0%EA%B8%B0%EC%B4%88-try-catch-finally

 

CH02 코틀린기초 - try catch finally

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다. 1. 코틀린도 자바와 다르게 체크 예외와 언체크 예외

colinkang.tistory.com

 

 

 

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석 역, 『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

discuss.kotlinlang.org/

 

Kotlin Discussions

Kotlin is a statically typed programming language for the JVM, Android and the browser sponsored and developed by JetBrains.

discuss.kotlinlang.org

stackoverflow.com/questions/tagged/kotlin

 

Newest 'kotlin' Questions

Stack Overflow | The World’s Largest Online Community for Developers

stackoverflow.com

www.reddit.com/r/Kotlin/

 

Kotlin • r/Kotlin

Discussion about Kotlin, a statically typed programming language for the JVM, Android, JavaScript, and native.

www.reddit.com

www.facebook.com/groups/kotlinkr

 

Facebook 그룹

Kotlin Korea에 멤버 4,313명이 있습니다. Kotlin 언어에 관심있는 사람들이 모인 Kotlin 한국 개발자 그룹입니다. 홈페이지: http://kotlin.kr 공식사이트: http://kotlinlang.org/ 페이지: https://www.facebook.com/kotlinkr

www.facebook.com

try.kotlinlang.org/

 

Try Kotlin

Try Kotlin right in the browser.

try.kotlinlang.org

github.com/jetbrains/kotlin

 

JetBrains/kotlin

The Kotlin Programming Language. Contribute to JetBrains/kotlin development by creating an account on GitHub.

github.com

github.com/jetbrains/exposed

 

JetBrains/Exposed

Kotlin SQL Framework. Contribute to JetBrains/Exposed development by creating an account on GitHub.

github.com

 

github.com/kotlin/anko

 

Kotlin/anko

Pleasant Android application development. Contribute to Kotlin/anko development by creating an account on GitHub.

github.com

kotlinlang.org/docs/reference/functions.html

 

Functions: infix, vararg, tailrec - Kotlin Programming Language

 

kotlinlang.org

kotlinlang.org/api/latest/jvm/stdlib/

 

kotlin-stdlib - Kotlin Programming Language

 

kotlinlang.org

Trove4J :  원시타입으로 된 collection library

bitbucket.org/trove4j/trove/src/master/

 

Bitbucket

 

bitbucket.org

kotlin.link/

 

Kotlin is Awesome!

 

kotlin.link

https://github.com/kotlintest/kotlintest 

 

kotest/kotest

Powerful, elegant and flexible test framework for Kotlin - kotest/kotest

github.com

 

 

책 사이트

www.manning.com/books/kotlin-in-action

kotlin-in-action 전체 예제 코드

www.manning.com/downloads/1271

아래의 글은 드미트리 제메로프, 스베트라나 이사코바 저/오현석역,『Kotlin in Action』,에이콘출판사(2017)의 내용을 기반으로 작성하였습니다.

 

kotlin은 저자가 밝혔듯이 러시아 상트페테르부르크 근처의 섬 이름이다.

위치는 북유럽 서쪽이다.

코틀린 섬

 

코틀린

 

1. 개발언어, jetbrain에서 개발하였음

2. JVM 기반 언어

3. 자바코드 및 라이브러리와 interoperability

4. 자바가 사용되고 있는 모든 용도에 적합하면서도 더 간결하고 생산적이며 안전한 대체 언어를 제공하는 것

5. javascript로도 코틀린 컴파일 가능, 코틀린 코드를 브라우저나 노드에서 실행할 수 있다.

6. 정적타입 : 컴파일 타임에 데이터의 타입이 정해짐, 컴파일 때 오류를 검출 할 수 있음. ( 타입 선언 생략 가능, 타입 추론 엔진)ene

7. class, inteface, generics 지원

8. function type 지원

9. annotation 지원

10. 함수형 및 함수타입

 

 

코틀린 프로그래밍

1. 서버프로그래밍 : 웹 어플리케이션, api 어플리케이션, RPC 프로토콜 지원 (기존 자바 라이브러리 혹은 프레임워크 통합 가능)

2.안드로이드 프로그래밍 : 모바일 어플리케이션 개발, 

 

참고로 코틀린 프로그래밍을 한다고 해서 서버 프로그래밍과 안드로이드로 앱 개발을 동시에 할 수 있을 거라는 착각은 하지 말자.

물론 같은 코드로 쓰여져 있으니 읽을수는 있지만 이해한다고는 볼수 없기 때문이다. 

사용해야하는 라이브러리가 다르며, 그리고 서버 프로그래밍과 클라이언트 UI 프로그래밍은 라이프사이클 관리가 다르며 데이터에 접근법이 다르므로 서로 다른 분야의 전문가이다. 하지만 둘이서 만나서 같은 코틀린으로 되어 있다면 이전보다는 서로를 이해하기가 좀 더 쉬울 것이라는 긍정적인 측면이 있다고 생각한다.

 

 

 

 

예제

 

fun main(args: Array<String>) {
    
    val personList = listOf(Person("Colin"), Person("Jina", 20))
    
    val oldest = personList.maxBy { it.age ?: 0}
    println("Hello, world! $oldest")
}

 

코틀린 특징

1. 코드가 짧아진다.

2. 연산자 오버로딩이 된다. (연산자 정의는 허용하지 않는다. cf. 스칼라는 연산자 정의가 가능하다.)

3. .kt라는 확장자 컴파일 하면 .class가 된다.

4. .class된 파일을 .kt로 디컴파일이 될까? (즉, .java > .class > .kt 가 가능할까? 좀 더 테스트 해봐야 겠지만 검색해봤을때는 가능한 것으로 보인다.)

5. 코틀린 대화형 쉘 : REPL ( ㅎ 이것도 요즘 언어의 유행일세 )

6. jave to kotlin 변환기 ( 자바코드 > 코틀린 변환 ㅎㅎㅎ 역시 다 있네 intellij code > convert java to kotlin , tool > kotlin > show kotlin bytecode > decompile )

try.kotlinlang.org/#/Kotlin%20Koans/Introduction/Java%20to%20Kotlin%20conversion/Task.kt

blog.mindorks.com/how-to-convert-a-kotlin-source-file-to-a-java-source-file

 

How to convert a Kotlin source file to a Java source file?

In this blog, we will learn how to convert a Kotlin source file to a Java source file. We will do the file conversion by two methods. So, let's see how.

blog.mindorks.com

 

'독서관련 > Kotlin in Action' 카테고리의 다른 글

CH02 코틀린기초 - 맵이터레이션  (0) 2021.01.01
CH02 코틀린 기초 - loop  (0) 2021.01.01
CH02 코틀린 기초 - 스마트 캐스트  (0) 2020.12.30
CH02 코틀린 기초  (0) 2020.12.28
CH00 kotlin useful info  (0) 2020.12.27
아래의 글은 마틴 파울러 지음 김지원 옮김, 『리팩토링 코드 품질을 개선하는 객체지향 사고법』,한빛미디어(2012)의 내용을 기반으로 작성하였습니다.

 

이름 요약 동기 예제
Extract Method
메서드 추출
코드의 뭉치를 별도의 메서드로 빼내는 기법 가장 많이 사용하고 핵심적인 기법
메서드 이름을 이해하기 쉽게 작성


Inline Method
메서드 내용 직접 삽입
메서드 내용이 너무 단순해서 메서드 명만 봐도 너무 뻔할 땐 그 메서더의 기능을 호출하는 메서드에 넣어버리고 그 메서드는 삭제하자 메서드를 간결하게 만드는 것
과다한 인다이렉션과 동시에 단순 위임이 너무 많을 때 사용
int getRating() {
return moreThanFiveLaterDeliveries() ? 2 : 1
}
boolean moreThanFiveDeliveries() {
return numberOfLateDelivereis > 5;
}

RF >

int geetRating() {
return numberOfLateDeliveries > 5 ? 2 : 1
}
Inline Temp
임시변수 내용 직접 삽입
간단한 수식을 대입 받는 임시변수로 인해 임시변수를 참조하는 부분을 전부 수식으로 치환   double basePrice = anOrder.basePrice();
return basePrice > 1000;

RF >

return anOrder.basePrice() > 1000
Replace Temp with Query
임시변수를 메서드 호출로 전환
수식의 결과를 저장하는 임시변수가 있을 땐 그 수식을 빼내어 메서드로 만든후 교체 임시변수에 값이 한번만 대입되고 대입문을 이루는 수식에 문제가 없는 경우 double basePrice = quantity * itemPrice;
if ( basePrice > 1000 ) 
return basePrice * 0.95;
else 
return basePrice * 0.98;

RF >

if (basePrice() > 1000) 
return basePrice * 0.95
else 
return basePrice * 0.98

double basePrice() {
return quantity * itemPrice;
}


Introduce Explaining Variable
직관적 임시변수 사용
수식이 복잡해서 이해하기 어려울 경우   if ( platform.indexOf("MAC") > -1) && resize > 0) {
}

RF >

boolean isMac = platform.indexOf("MAC) > -1;
boolean wasResized = resize > 0;

if ( isMac && wasResized ) {
}

Split Temporary Variable
임시변수 분리

루프변수나 값 누적용 임시변수가 아닌 여러 번 값이 때입될땐 각 대입마다 다른 임시변수 사용   double acc = primaryForce / mass;

if ( secondTime > 0) {
acc = primaryForce + secondaryForce / mass;
}

RF >

if (secondTime > 0) {
final double secondaryAcc = primaryForce + secondaryForce / mass;
}
Remove Assignments to Parameters
매개변수로의 값 대입 제거
매개 변수로 대입하는 코드가 있을 땐 매개변수 대신 임시변수를 사용하자   int discount ( int inputVal, int quantity, int yearToDate) {
if (inputVal > 50) inputVal -= 2;
return inputVal;
}
RF >

int discount(int inputVal, int quantity, int yearToDate) {
int result = inputVal;
if ( inputVal > 50 ) result -= 2;
return result;
}

Replace Metod with Method Object
매서드를 매서드 객체로 전환
지역변수 때문에 매서드 추출을 적용할 수 없는 긴 메서드가 있을때 그 메서드 자체를 객체로 전환해서 모든 지역변수를 객체의 필드로 만들고 그런 다음 그 메서드를 객체 안의 여러 매서드로 쪼개기    
Substitude Algorithm
알고리즘 전환
알로리즘을 더 분명한 것으로 교체해야 할 땐, 해당 메서드의 내용을 새 알고리즘을 바꾸자   String foundPerson(String[] people) {
for (String p : people) {
if (p.equals("Don") return "Don";
if (p.equals("John") return "John";
return "None";
}

RF >

String foundPerson(String[] people) {
List matchName = Arrays.asList("Don", "John");
for (String p : people) {
if (p.contains(matchName(i)) return matchName(i);
}
}

 

아래의 글은 마틴 파울러 지음 김지원 옮김, 『리팩토링 코드 품질을 개선하는 객체지향 사고법』,한빛미디어(2012)의 내용을 기반으로 작성하였습니다.

 

저자도 설명하였지만 역시나 설명보다는 코드가 더 이해하기가 쉽다.

 

github.com/colinktkang/refactoring

 

colinktkang/refactoring

Contribute to colinktkang/refactoring development by creating an account on GitHub.

github.com

 

 

'독서관련 > 리팩토링 - 2012 - 마틴 파울러' 카테고리의 다른 글

CH06 메서드 정리  (0) 2020.12.12
CH04. 테스트 작성  (0) 2020.12.12
CH03. 코드의 냄새  (0) 2020.12.12
CH02. 리팩토링 개론  (0) 2020.12.12
CH01. 예제를 통한 리팩토링 개념 잡기  (0) 2020.12.07
아래의 글은 마틴 파울러 지음 김지원 옮김, 『리팩토링 코드 품질을 개선하는 객체지향 사고법』,한빛미디어(2012)의 내용을 기반으로 작성하였습니다.

 

리팩토링을 실시하기 위한 필수 전제조건은 견고한 테스트 코드를 작성해야하는 것.

 

테스트 코드의 가치

 

컴파일 혹은 빌드시마드 테스트 수행 : 생산성을 높임, 버그를 찾는 시간이 줄고, 코드 영향도를 파악하기가 쉽고, 

 

테스트는 완전히 자동화 하고 결과를 자체적으로 검사하게 하자

 

테스트 스위트는 버그를 찾는 시간을 획기적으로 줄여주는 강력한 버그 감지 도구다.

 

리팩토링이 안되면 테스트 코드 작성하는 것도 어렵다.

 

리팩토링이 되면 테스트 코드 작성하는 것도 쉽다.

 

 

+ Recent posts