일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Callback
- Exposed Drop-Down Menu
- Kotlin
- 앱개발
- Dialog
- 안드로이드 스튜디오
- 앱 출시
- 안드로이드 api
- android api
- android studio
- 비동기
- 공유 기능
- 플레이 콘솔 프로덕션
- Retrofit
- urlconnection
- 플레이스토어 앱 게시 문제
- Bottom sheet
- 안드로이드 http
- 안드로이드스튜디오
- Today
- Total
Strong 감자의 공부
Ch03_문장 제어 처리 알아보기 본문
-목차 -
1. 조건표현식 알아보기
-비교 연산자
-논리 연산자
-동등성
2. 조건문 알아보기
-if
-when
-예외
3. 순환표현알아보기
-범위
-for, while, do while
-반복자+ 고차함수 조금
1. 조건 표현식 알아보기
1. 비교연산자
연신지 | 메서드전횐 |
> | a.compareTo(b)>0 |
< | a.compareTo(b)<0 |
≥ | a.compareTo(b)≥0 |
≤ | a.compareTo(b)<=0 |
== | a?. equals(b)?: (b===null) |
! = | !(a?.equals(b) ?: (b===null) |
b===null 중 === 은 참조값(=주소값 비교 )
a?.equals(b)에서 ?.은 a가 null일경우 . 만 있을 시 널예외가 발생함. 그래서 a가 널이라면 .?에의해 뒤의 메소드 연산을 하지 않는다.
fun main() {
val a = 100
val c = null
val d = null
println(c==d) // 결과값 true
println(c===d) // 결과값 true
println(c?.equals(d)) // 결과값 null
println(c?.equals(d) ?: (c == null)) // 결과값 true
}
+ 자바에서 참조 타입인 두 피연산자 사이에 ==를 사용할 경우
자바에서
참조 타입인 두 피연산자 사이에 ==를 사용할 경우 주소값으로 비교를 하게 된다. 두 피연산자의 주소값이 같은 곳을 가리키고 있다면 true를 반환하는 것이다. String의 경우 원시 타입이 아닌 참조 타입이기 때문에, 겉으로 보이는 문자가 똑같아 보여도 주소값이 다를경우 false가 출력된다.
String a = "hi" // 주소값 : 1번지
String b = "hi" // 주소값 : 2번지
System.out.println(a==b) // false
따라서 자바에서는 두 객체(참조 타입)의 동등성을 알기 위해서 equals를 호출해야 한다.
String a = "hi" // 주소값 : 1번지
String b = "hi" // 주소값 : 2번지
System.out.println(a.equals(b)) // true
코틀린에서도 == 연산자가 기본이다. 그러나 자바와는 동작 방식에 조금 차이가 있다. 원시 타입 두개를 비교할 때는 == 연산자가 동일하게 동작하지만, 참조 타입을 비교할 때 다르게 동작한다.
==는 내부적으로 equals를 호출한다. 따라서 참조 타입인 두 개의 String을 ==연산으로 비교하면 주소값이 아닌 값(동등성)비교를 한다.
val a: String = "hi"
val b: String = "hi"
println(a == b) // true
참조 타입의 주소 값을 비교(reference comparision)하고 싶다면?
코틀린은 자바에는 없는 ===연산자를 지원한다. 참조 비교를 위해서 === 연산자를 사용하면 된다. 즉, 자바의 주소 값 비교인 ==와 코틀린의 ===가 동일한 역할을 한다.
그러면 String 객체 a,b 에 대하여 a === b 는 false 가 되어야하지 않나요? 라고 생각할 수 있다. 하지만 Stirng 객체는 String pool 이라는 힙의 영역이 따로 존재하여 값을 String pool에 저장한다. 만약 이미 String pool 에 같은 값이 존재한다면 같은 주소를 참조하는 방식이다. 그래서 a === b 값이 true를 반환하게 된다. 즉, String class 에는 equals 를 객체의 equals 를 override(재정의) 한 것이다
출처-
https://wooooooak.github.io/kotlin/2019/02/24/kotiln_%EB%8F%99%EB%93%B1%EC%84%B1%EC%97%B0%EC%82%B0/
+스트링 수정과 참조
String 문자열은 참조 타입이므로, 문자열 뒤에 문자열을 추가하는 것 같은 수정 작업을 한다면,
기존에 참조하고 있던 String Pool 영역에 저장된 문자열에 추가하는 것이 아니라, 추가된 문자열을 새로 String Pool에 생성(저장)하여 참조를 이곳으로 바꾸는 것이다
+ 스트링 및 클래스 객체끼리 == ,=== 할 때
class UserA(var id: String)
class UserB(var id: String) {
override fun equals(other: Any?): Boolean {
if (this === other)
return true
other as UserB
if (id != other.id)
return false
return true
}
//레퍼런스를 정수로 바꿔줌
override fun hashCode(): Int {
return id.hashCode()
}
}
fun main() {
val one = "ug"
val two = "ug"
println("one==two : " + (one == two))
println("one===two : " + (one === two))
var user1 = UserA("0")
var user2 = UserA("0")
println("user1 == user2 :" + (user1 == user2))
println("user1 === user2 :" + (user1 === user2))
var user3 = UserB("1")
var user4 = UserB("1")
println("user3 == user : " + (user3 == user4))
println("user3 === user : " + (user3 === user4))
}
객체에서 equals 는 주소 비교 와 같다. 객체의 값을 비교하려면 equals 를 override 해야 한다.
equals를 override 하기 위해서는 hashCode 도 override 해야됨.
⚫ 실수도 숫자이므로 하나의 숫자인 객체가 유일하다 .
val d= 0.0
val e = -0.0
println(d==e) // 결과값 true
println(d===e) // 결과값 true
하지만 숫자나 문자열은 항상 값과 레퍼런스가 동일해야 하지만, 성능을 위해 필요할때마다 여러개를 만들어 처리 할 수 있다. 그래서 값이 같지만 레퍼런스는 가티 않을 수 있으므로 값만 비교하는 것을 권한다고 책에 써있다.
compare 메서드의 결과는 0, 음수, 양수이다.
후에 Comparator Comparable 메서드 구현할때 필요했다.
결과값 | 설명 |
0 | 두항이 같다 |
- | 앞항이 작다. |
+ | 앞항이 크다. |
2. 포함연산자처리
연산자 | 표현식 | 메서드 |
in | a in b | b.contains(a) |
!in | a ! in b | !b.contains(a) |
- any, all, none처리
- all : 모든것이 참인 경우
- none : 모든것이 거짓인 경우
- any : 하나라도 참이 경우
- 여러 원소를 가진 배열이나 리스트 등에 특정 원소의 값이 있는지 확인해서 논리값으로 처리하는 메서드
fun main() {
val height = 46
println(" 1<=height<=46 ? : " + (height in 1..46)) // true
val list =listOf(1, 2, null)
println(list.all({ it== null})) // false
println(list.none({ it== null})) // false
println(list.any({ it== null})) // true
}
3. 논리 연산자처리
연산자 | 의미 | 메서드 |
&& | 둘 다 참이여야 참 그외 거짓 | (a>b) and (a<c) |
! | !a : a가 참일때, !a는 거짓처럼 반대로 바꿔버림 | !a |
4. 함수도 일급객체(=영어: first-class object 란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체)
함수 참조 : 함수 정의 후 메모리에 로딩된 함수의 주소를 가져오는 리플렉션( :: ) 기능
함수도 객체라 레퍼런스 가지고
변수에 할당할 때 함수 참조를 사용해서 함수 레퍼런스를 가져옴.
해시코드로 변환하면 정수로 표시돼 쉽게 비교 가능
fun add(x: Int, y:Int) = x+y // 함수정의 시 동일한 참조 갖음.
fun main() {
val addVal = ::add
println((::add).hashCode() == addVal.hashCode()) // true
println(addVal(10, 20)) // 30
}
2. 조건문 알아보기
1. if
a. 단순 조건처리
fun main() {
val a = if (10 > 20) "성공" else "실패"
print(a) // 실패
}
위의 예처럼 표현식 처리시 반드시 if else를 사용하고
표현식으로 처리할 때는 변수(위의 예에서 a )에 할당이나 반환이 필요
이를코드블록으로 처리 할 경우에도 반드시 마지막에 반환값표시(아래 예)
fun main() {
val a = if (10 > 20) {
println("참")
true // 반환값 표시
} else {
println("거짓")
false // 반환값표시
}
println("변수 $a")
}
// 결과값
// 거짓
// 변수 false
b. 블록 구문 내의 지역변수
지역변수와 전역변수를 같은 이름으로 선언했다. 같은 이름이지만 관리하는 영역이 다르기 때문에 별도의 변수로 관리함.
블록 구문내에서 지정한 변수를 사용하면 전역변수에 접근할 수 없어서 지역변수에 할당된 값을 츌력한다.
fun main() {
val x = 100
var circle = 2
// 전역변수 참조
while (circle-- > 0) { // ...1
if (x == 100) {
val x = 200
println("참 $x")
} else {
println("거짓 $x")
}
val x = 400
println(x)
}
circle += 3 //... 1 에서 0이됐을때도 확인하기 때문에
// 지역변수 참조
while (circle-- > 0) {
val x = 300
if (x == 100) {
val x = 200
println("참 $x")
} else {
println("거짓 $x")
}
}
}
출력값
참 200
400 참 200
400 거짓 300 거짓 300
→가장 가까운 변수의 값을 출력하고 {} 블록이 끝나면 지역변수들을 참조할 수 없는것 같다.
2.when 조건
- 단일 특정 매칭 예) 1 -> println("1core")
- 복합 특정 매칭 예) in 2..16 -> println("$cores Cores")
- 패턴이 없는경우 예) else -> println("I want your machine")
⚫ 조건 → 처리된 결과 순
fun main() {
val cores = Runtime.getRuntime().availableProcessors() // 노트북 코어 수 읽기
when (cores) {
1 -> println("1core")
in 2..16 -> println("$cores Cores")
else -> println("I want your machine")
}
}
// 결과
8 Cores
1, in 2..16 들(화살표 왼쪽 )을 패턴이라고 하는 것 같다.
⚫ when도 표현식으로 처리가능이라 결과값을 변수에 할당가능하다.
fun main() {
val cores = Runtime.getRuntime().availableProcessors()
val result: String = when (cores) {
1 -> "1core"
in 2..16 -> "$cores Cores"
else -> "I want your machine"
}
println(result)
}
// 결과
8 Cores
⚫직접함수 반환처리
fun main() {
println(sys())
}
fun sys(): String = when (val cores = Runtime.getRuntime().availableProcessors()) {
1 -> "1core"
in 2..16 -> "$cores Cores"
else -> "I want your machine"
}
// 결과
8 Cores
3. 예외
a. 예외처리 : try catch finally(얘는 무조건실행)
fun main() {
try {
add()
} catch (e: Exception) {
println(e)
} finally {
println("정상처리")
}
}
fun add() = 100
//결과
정상처리
b. 예외 던지기 : throw
fun main() {
try {
throw Exception("예외 발생")
} catch (e: Exception) {
println(e)
} finally {
println("정상처리")
}
}
//결과
java.lang.Exception: 예외 발생
정상처리
c. 예외도 표현식이라 변수 할당이 가능하며, 예외처리는 비정상 처리를 정상 처리로 변환해준다.
fun main() {
val x = try {
100
} catch (e: Exception) {
200
} finally {
300
}
println(x)
}
//결과
100
아래의 경우 “예외”라는 글자는 안나옴
fun main() {
val y = try {
add_ex()
} catch (e: Exception) {
200
} finally {
300
}
println(y)
}
fun add_ex(): Nothing = throw Exception("예외")
//결과
200
3. 순환표현 알아보기
1. 범위객체 생성하기
fun main() {
val r1 = 1..10
val r2 = 1.rangeTo(10)
println(r1 == r2) // true
println(r2.first) // 1
println(r2.last) // 10
val r3 = 1.until(10) // 마지막 항목 포함x
println(r3.first) // 1
println(r3.last) // 9
val r4 = 10.downTo(1)
println(r4.first) // 10
println(r4.last) // 1
val r5 =1.rangeTo(10).step(2)
println(r5.javaClass.kotlin) // class kotlin.ranges.IntProgression
println(r5.first) // 1
println(r5.last) // 9
println(r5.step) // 2
}
2. 순환
a. 순환조건은 in을 기준으로 앞에는 지역변수, 뒤에는 컬렉션 클래스의 객체인 범위, 배열, 집합, 맵, 등이 온다.
fun main() {
for (i in 1..5)
when (i) {
in 1..4 ->print(i.toString() + ",")
else ->print(i.toString())
}
}
// 결과
1,2,3,4,5
b. 범위의 시작과 끝이 같다면 한번만 실행
fun main() {
for (i in 5..5)
println(i.toString() + ",") // 5,
for (i in 5..5step3)
println(i.toString() + ",") // 5,
}
var a =10
println(a + “,”) (x)
println( “,”+ a) (0)
c. for 역방향 순환
fun main() {
for (i in 10..1)
print(i.toString()) // 결과값 없음
println()
for (i in 10.downTo(1))
print(i.toString()) // 10987654321
println()
for (i in 10.downTo(10))
print(i.toString()) // 10
}
d. 문자 범위 순환
문자도 아스키코드나 유니코드에 순서대로 저장되어 있어 문자범위를 만들 수 있다.
fun main() {
for (ch in 'a'..'f')
print(ch.toString()) // abcdef
println()
for (ch in 'f' downTo 'a')
print(ch.toString()) // fedcba
}
e. continue / break : 가장가까운 순환문에서 다음으로 이동/ 가장가까운 순환문 종료
f. break@레이블, continue@레이블 : 중첩된 순환문에서 한번에 외부순환문까지 (끝낸다.)빠져나올 수 있다.
fun main() {
loop@ for (i in 1..3) {
for (j in 1..5) {
if (j == 2) {
println("break호출")
break@loop
}
println("내포순환" + j.toString())
}
}
}
// 결과
내포순환1
break호출
3. while(){} /do{}while() : 조건()을 만족하면 순환문 내부 실행/ 일단 한번 실행 후 조건() 확인
a. break@레이블, continue@레이블은 여기서도 사용할 수 있다.
fun main() {
var n = 0
var m = 0
ug@ while (n++ < 3) {
println("n : " + n)
while (m++ < 5) {
println("m :" + m)
if (m == 3) break@ug
}
}
}
// 결과값
n : 1
m :1
m :2
m :3
https://parkjuida.tistory.com/37
4. 반복자
a. 반복형(iterable) 클래스 : 여러개의 원소를 가진 자료형인 범위, 배열, 리스트 등을 객체로 만듦
b. iterator메서드 : 반복자(Iterator) 클래스의 객체로 변환 가능
반복형 → 반복자 자료형 변환 시 내부의 원소를 순환 할 수 있는 메서드 추가
c. 보통 내부 순환을 하는 forEach, map, filter, reduce등 메서드 제공
⚫ forEach{ }
반복자 생성 후 반복자.forEach{it. }
fun main() {
val i = 1..10
val c = 'a'..'z'
// 반복자 객체 생성
val iter1 = i.iterator()
val iter2 = c.iterator()
iter1.forEach{print(it.toString() + ",")}
println()
for (i in iter2) print(i + ",")
}
// 결과값
1,2,3,4,5,6,7,8,9,10,
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,
fun main() {
val r = ('a'..'c').iterator() // 반복자(Iterator) 처리
while (r.hasNext())
println(r.next())
('d'..'e').forEach(::print)
println()
('f'..'g').forEach(::println)
}
// 결과값
a
b
c
de
f
g
⚫ map
반복자 생성없이 반복형에 바로 사용
map은 콜백이 객체를 반환할 것으로 예상하기 때문에 아래 코드 예제와 같이 완전히 새로운 객체를 만드는 데 사용할 수 있습니다.
배열의 모든 항목을 포함하지만 대신 콜백 함수가 이 경우 이름이 될 원래 동물 대신 새 배열에 넣을 변환된 개체를 반환할 것으로 예상.
map은 콜백이 객체를 반환할 것으로 예상하기 때문에 아래 코드 예제와 같이 완전히 새로운 객체를 만드는 데 사용할 수 있습니다.
data class Animals(var name: String, var species: String)
var animals=arrayOf(
Animals("Poppy", "rabbit"),
Animals("Caro", "dog"),
Animals("Teddy", "dog"),
Animals("Molly", "fish"),
Animals("Jimmy", "cat"),
Animals("Harold", "fish")
)
fun main() {
var names =animals.map(
fun(animal: Animals): String {
return animal.name
})
println(names)
}
// 출력값
[Poppy, Caro, Teddy, Molly, Jimmy, Harold]
⚫ filter
반복자 생성없이 반복형에 바로 사용, 필터는 항목이 배열에 포함되어야 하는지 여부를 결정하는 참 또는 거짓 값을 반환하는 콜백 함수
fun main() {
val r = (1..10)
val result = r.filter{ it% 3 == 0}
println(result)
}
// 결과값
[3, 6, 9]
map은 배열을 가져 와서 동일한 길이의 배열로 변환하지만 각각의 개별 항목이 변환됩니다.
filter는 배열을 더 작은 배열로 변환합니다.
⚫ reduce
반복자 생성없이 반복형에 바로 사용
varamounts=arrayOf(
256,
45,
344,
775,
121,
50
)
fun main() {
var totalAmount =amounts.reduce(fun(sum, amount): Int {
return sum + amount
})
println(totalAmount)
}
//결과값
1591
val totalAmount = amounts.reduce { sum, amount -> sum + amount }
sum은 첫번째 콜백 인자이고 amout는 두번째 인자로 iterated item이다.
https://medium.com/mindorks/functional-programming-in-kotlin-part-2-map-reduce-ebdb3ebaa1f6
🌻🌻스터디 후기 🌻🌻(현재 추가중)
1.
when (cores) {
1 -> println("1core")
in 2..16 -> println("$cores Cores")
else -> println("I want your machine")
}
+구문에서 else는 위에 설정된 모든 조건에 해당하지 않을 경우에 해당하며, 컴파일러는 else 가 있는지를 강력하게 확인하고, else 가 마지막이 아닌 부분에 오는 것은 허용하지 않습니다.
버전에 따라 else를 안써도 됐었지만 지금 나오는 버전에선 써야한다.
2. Unit/ Nothing의 차이
요약하면 Unit은 자바로는 void로 정상적인 종료인데 반환값 자체가 없을 때 쓰이고,
Nothing은 예외 처리나 에러를 던지는 상황에서 그 함수에서 아무것도 반환할 상황이 아닐때 쓰인다고 했는데
제너릭관련해서도 얘기가 나와 추후 제너릭을 나가면 그때 포스팅을 할 것입니다!
-코드
3. :: 언제 왜 쓸까?
4. 코드 컨벤션 - 사람마다 다른부분이지만 멘토님은 이런식으로 쓰신다고 예를 들어주셨고
const val EXTRA_NAME = "name"
const val ARG_NAME = "name"
추가로 이렇게 상수로 선언해야하는 이유에 대해선 개발자들의 실수를 줄일 수 있기 때문이라고 설명해주셨다. 그래서 멘토님은 한번만 쓰이는 것이라도 상수로 선언하신다고 말해주셨다.
val intent = Intent()
intent.putExtra("name1", "hello") // name1을 누가 실수로 naaam으로 고칠 수도 있음.
intent.putExtra("KEY_NAME1", "hi")
'CS > 문법_Kotlin' 카테고리의 다른 글
Ch_07 클래스 관계 등 추가 사항 알아보기 (0) | 2023.08.25 |
---|---|
Ch_ 06 내장자료형 알아보기 (0) | 2023.08.19 |
Ch_05 클래스 알아보기 (0) | 2023.08.11 |
Ch_04 함수 알아보기 (0) | 2023.08.04 |
Ch02_ 코틀린에서는 모든것이 객체이다. (0) | 2023.07.19 |