항공대학교 김철기 교수님 객프 수업을 정리한 내용입니다. 내가 나중에 보려고 쓴 거라 조금 난잡함 주의 🤐
<객체지향 프로그래밍 특징>
- 자료 추상화가 가능하다.
private 등으로 불필요한 정보는 숨기고 중요한 정보만 노출할 수 있다. - 상속
연관된 클래스 간에 상하위 연관성을 둘 수 있다. - 다형성
오버라이딩 override / 오버로딩 overloading 이 가능하다.
<KOTLIN 특징>
-안전성 : 타입추론 가능, Null Pointer Exception 예방, Smart Cast를 통한 안전한 타입 변환 가능
-다중 패러다임 : 함수형 패러다임, 동시성 프로그래밍
-간결성, 표현력 좋음
-상호 운용성 / 다중 플랫폼
<변수>
- 타입 추론이 가능하다.
val n = 15
굳이 val n : Int = 15 로 쓰지 않아도 된다.!
val text = "apple"
- 초기값 생략
초기값 생략 시에는 무조건 TYPE 명시해야 한다.
val n:Int
단, 변수에 값을 대입하지 않은 상태에서 연산 등 하면 안된다ㅠㅠ 컴파일 오류 발생
(기존 프로그래밍의 경우 null로 초기화를 해주지만 여기에서는 null pointer exception을 원천적으로 차단한다.)
<연산자 우선순위>
- 우선순위를 감안해서 해석해본다면??
(a<b) == (b<c)
(a==b) and (b==c)
(a<(b or b))
(a==b) || (b!=c)
a=(b*c)
<기본타입>
Byte | 1바이트 | -128~127 |
Short | 2바이트 | -32678~32676 |
Int | 4바이트 | -2**31~2**31-1 |
Long | 8바이트 | -2**63 ~ 2**63-1 |
- 표현방법
val m = 332_856 //이건 Int인데 이런식으로 ,를 _로 표현할 수 있다.
val hundred1 = 100L // Long타입 - 최솟값 최댓값 외우기 너무 어렵다면 출력해서 알아볼 수 있음
Short.MIN_VALUE
Int.MAX_VALUE - 부동소수점 수 Double (64비트) / Float(32비트)
기본적으로 소수점 있는 수를 만들면 자동 Double이다. float를 만들고 싶다면 3.14f 이런식으로! - Infinity 표현 가능
100.0/0.0 = Infinity
-100.0/0.0 = -Infinity - 문자 타입 Char
val z = 'z'
val pi = '/u03C0' //이건 특수문자 - 수변환도 가능하다.
945.toChar() //이러면 α가 자동으로 나옴
(-2.45).toInt() //-2로 반올림한 결과가 나온다. 그냥 toInt는 소수부분을 삭제★해준다고 생각하자. - 형변환은 자동으로 큰 것으로 해주고, 자료형이 서로 다르다고 해도 크기 비교가 가능하다.
<비트 연산>
- 일단 이거부터 알아두자!
13: 000000001101
-13: 111111110011
그냥 (NOT 13) + 1 한다고 생각하자! -- 2의 보수법 참고
<비교와 동등성>
- NaN은 어떤 수와도 같지 않고, 어떤 수보다 크거나 작지 않다. 바보 같지??
Double.NaN.isNaN만 True로 가능하다!
<문자열>
- Naming Convention
변수명, 함수명은 camelCase를 이용
클래스이름은 PascalCase를 이용
상수는 전체를 대문자로 하는 snake_case를 이용 - 문자열은 배열이 아니다! String객체라고 생각해야 한다.
\이스케이프 시퀀스를 이용하여 특수문자를 표현할 수 있다. 만약 \이스케이프 문자를 사용하고 싶다면 val message = """ Triple quote : '${"\"\"\""}' """ 이렇게!
<문자열 연산>
- 문법
"Hello".length
"Hello".lastIndex
"Hello".substring(2) //"llo"
"Hello".startsWith("Hell") //true
"abcabc".indexOf("ab") // - 문자열 + 숫자는 형변환이 자동으로 문자열로 된다!
val str = "The sum is: " + sum - 문자열의 의미상 동등성 비교에는 "==" 또는 "!="를 사용하고, 동일 객체인지 비교할 시에는 "===" 또는 "!==" 사용
- 문자열은 사전상 순서를 비교할 때도 사용 가능하다.
print("abc" < "cba") //true
<배열 Array!!>
val a = emptyArray<String>()
val b = arrayOf("hello", "world")
val c = arrayOf(1,4,'9') -> 이럴 경우 타입은 Any로
람다함수로도 만들 수 있다.
val squares = Array(10) {(it + 1) * (it + 1)}
val operations = charArrayOf('+', '-', '*')
만약 배열을 똑같이 가져와 쓰고 싶다면, (공유없이)
val numbers = squares.copyOf() //copyOf 함수 사용!
copyOf(2)라면 앞에서 2개 원소만 배열 사본 생성
- 타입이 한 번 정해지면 바꿀 수 없다!!
- 배열을 생성하면 그 길이를 바꿀 수 없지만 + 연산자를 사용해서 원소를 추가한 '새로운' 배열을 생성할 수 있다.!
val a = intArrayOf(1,3,4)
val b = a+4 //이래도 4를 넣어줌 - 배열 비교
'==' '!=' 연산자는 동일 객체인지 여부만 판단해준다. (여기가 String과 좀 다름)
내용이 같은지 판단할 때는 'contentEquals()'라는 함수 사용 - 메소드 종류
isEmpty
isNotEmpty
indexOf
<함수>
- 파라미터의 타입은 항상 지정!
fun circleArea(radius: Double): Double - 반환값의 타입을 생략할 수 있다.
반환값이 없는 경우. : Unit 이 사실 타입 생략의 표시
또는 식에서 반환값의 타입이 너무 유추가능할 때 즉, 그냥 Double*Double 했다. 이럴 때 생략 - 파라미터의 값은 수정이 불가능
fun foo(bar: Int) : Int {
val baz = bar+1
return baz
} - public 프로젝트의 어디서나 /internal 모듈 /private 같은 파일 안에서
<함수의 오버로딩>
- 함수 이름은 똑같은데 매개변수만 다르게 함으로써 메서드를 여러 개 만드는 것!
- 함수 호출 시 오버로딩 해소 규칙
파라미터 개수와 타입을 기준으로 호출할 수 있는 모든 함수를 찾는다.
덜 구체적인 함수를 제외시킨다. 어떤 함수의 파라미터의 타입이 다른 함수에 비하여 포괄적일 경우 이를 제외한다. 이 과정을 하나의 함수가 남을 때까지 반복
하나가 남으면 해당 함수 호출. 두 개 이상이 남으면 컴파일 에러 발생
<패키지>
- 동일 패키지의 함수는 import 없이 호출할 수 있다.
- 다른 패키지의 함수를 호출할 때에는 함수의 이름에 패키지 이름까지 모두 붙여서 호출. 만약에 함수이름까지 붙이면 함수이름만으로 호출 가능
- 다른패키지에 있는 동일한 이름의 함수 호출 시 둘다 함수까지 호출해야 하고 as 로 이름을 하나는 fooReadInt 또 하나는 barReadInt 이렇게 따로 지정해주자.
- 전체 import (*사용)도 가능하다. 구체적 import에 비해 우선순위가 밀린다.
<조건문>
- 만약 if문으로 값을 할당한다면 빠지는 게 있으면 안된다. if 에 else까지 값이 들어갈 수 있도록 할당되어야 함
- if문 아직 안 끝났지만 return으로 함수 탈출 가능
- when구문
- for문 outerloop
<범위 타입>
- 문자열 출력도 이런 식으로 됨
- until, downTo, step
10 until 100 (10 .. 99와 같음)
10 downTo 1 (10 .. 1과 같음) - 응용 가능
"Hello World".substring(1..4)
IntArray(10) {it * it}.sliceArray(2..5)
<클래스와 인스턴스>
- 클래스로 인스턴스를 만들 수 있다.
- 클래스 생성자?
기본생성자 : 클래스를 만들 때 기본으로 선언 init 초기화 하려는데, 하나의 식으로 표현 어려울 때. 반드시 모든 property는 초기화되어야 한다.(클래스에 val/var로 정의되는 변수를 프로퍼티라고 한다.) *만약 주생성자를 바로 만들고 싶다면 그냥 파라미터에 val/var 붙이면 됨 - 보조생성자 : 필요에 따라 추가적으로 선언. 하나 이상의 생성자를 정의하고 싶을 때. constructor(여기에 인자로 몇 개만 가져갈 수 있음) ..
//this는 생성자위임 호출이다.
- 클래스에서의 멤버 가시성
public : 어디서나
internal : 모듈 안에서
protected : 멤버가 속한 클래스와 그 하위 클래스 --> 나중에 클래스 상속에서 !!
private : 멤버가 속한 클래스 내부에서만
- 추가_로컬 클래스라는 정의에 대해 알아두자.
함수 내에서 class를 정의할 경우 이를 지역 클래스라 부르고 함수에서 멤버에 접근 가능
<내포된NESTED 클래스와 내부INNER 클래스>
- 중첩클래스는 외부 내용을 공유할 수 없지만, 내부 클래스는 외부 클래스의 속성과 함수의 사용이 가능하다.
<NULL>
- ?. null safe operator null이면 뒤에 꺼 안 함 let에서 사용 as?
- ?: elvis operator null이면 뒤에껄로 대체. 이 뒤에 return문 "Unknown" 이나 throw를 두기도.
- !!. non-null assertion operator null이면 exception 발생.
- 스마트 캐스트 - 프로그래머가 굳이 원하는 타입으로 캐스팅하지 않아도 컴파일러가 알아서 캐스팅해주는 것 의미 (원래는 논적으로 null인 상황을 제일 먼저 제거해야 한다. )
단 이 때 var 은 스마트캐스트 불가능.
<변수 초기화>
- 늦은 초기화 : 변수에 객체에 할당하는 것을 선언과 동시에 할 수 없을 때.
lateinit var text: String -- String 외 기본 자료형에는 사용 불가, 초기값 할당 전 변수 사용X
val a: Int by laxy{7} -- 실제 실행 시에는 var 변수를 '사용'하는 시점에 초기화 과정 진행.
여러 개의 구문이 들어갈 수 있되, 맨 마지막 값이 들어 됨.
<Property와 Field - 커스텀 접근자>
- field가 뭐지..?
프로퍼티가 가진 값이 저장되어 있는 곳이라고 생각하자. 메.모.리에 저장하는 형식이다.
field는 언급을 해야지만 뒷받침하는 필드가 생긴다.
- getter / setter
property 의 데이터 얻거나 변경하기 위해 사용되며 접근자(accessor) 라 한다. get() set()으로 구현
<Object>
- 생성자 없이 직접 객체를 만든다는 특징이 있다.
- 단 하나로 공통적인 속성과 함수를 사용한다면!? Singleton Pattern!! 오직 하나의 인스턴스
- 계산기
- class 안에서도 object를 만들 수 있다! companion object.
다른 인스턴스인데 공통적으로 접근하는 것 또한 있도록 만들기
- 객체식
명시적인 선언 없이 객체를 바로 생성할 수 있는 특별한 식
자바 익명 클래스와 비슷하다. 프로그램에서 일시적으로 한번만 사용되고 버려지며, 클래스 정의와 동시에 객체를 생성한다.
가시적임!!
<고체함수>
- 함수를 인스턴스처럼 사용
함수 타입 지정 방식은 위와 같다.
- b안에 a함수 있지롱~
- 람다를 사용한 중복 제거 (패러미터로 넘길 함수를 굳이 이름까지 만들어서 넘기지 않는다.
함수의 형식을 적지 않고, 바로 중괄호 안에 parameter type만 넣으면 알아서 추론해줌. - 컬렉션의 조작, 스코프 함수 사용에 도움 됨
- 반환값 앞에 : 아니고 ->
- 반환값 없을 때는 -> Unit 반드시 명시
- 프로그래머의 가시화를 위해 파라미터에 이름 붙이기 가능
op: (firstValue: Int, nextValue:Int -> Int) - 람다함수의 마지막 문장의 식이 람다 함수의 결과값 !
- 람다함수가 마지막 파라미터일 때 람다함수 바로 앞에서 괄호를 닫고 () {람다함수} 이렇게!
- 파라미터가 없는 람다함수 -> 생략 가능
- 파라미터 하나인 람다함수는 -> 이거 안 쓴다면, 파라미터 it으로 지칭
- 사용하지 않는 파라미터 _, index 이런 식으로 _로 표현
- 람다함수의 대안으로는 익명함수를 쓸 수 있는데
res, elm -> res+elm
fun(res, elm) = res+elm - 이미 add라는 함수가 존재한다면 ::add로 넘기면 된다. ::는 참조!
<확장함수 + 확장프로퍼티(getter,setter) +동반확장>
- 기존에 정의된 클래스에 함수를 추가
- util에 많이 만듦
- fun String?.truncate(maxLength:Int) = substring(0, maxLength)
"HELLO".truncate(3) - 확장프로퍼티
val IntRange.leftHalf: IntRange
get() = start.. (start + endclusive) /2 - 동반확장
동반객체의 확장도 가능하다. 동반객체애 대한 확장 프로퍼티도 가능
단, 동반 객체가 존재하는 경우에만 동반 객체 확장이 가능(예> Any는 모든 타입의 최상위 타입이므로 동반확장이 불가능)
<영역함수>
- 인스턴스의 속성이나 함수를 좀 더 깔끔하게 불러 쓸 수 있다. + 람다함수!!
- apply, run, with, also, let(널 안전성 처리)
apply 인스턴스 생성한 후 변수에 담기 전에 '초기화 과정'을 수행할 때 많이 쓰임
scope 안에서 참조연산자 없이 속성과 함수 사용 가능
생성되자마자 조작된다면 인스턴스에 바로 넣어줄 수 있음
also (it을 통해 인스턴스 사용 - 같은 이름의 변수나 함수가 scope 밖에 있을 때 예방)run 일반 람다함수처럼 마지막 구문을 결과값을 반환
이미 인스턴스가 만들어진 후에 인스턴스의 함수나 속성을 scope 내에서 사용해야 할 때 유용
a.run { … }
let (it을 통해 인스턴스 사용)with run과 동일. 단지 인스턴스를 참조연산자 대신 파라미터로 받음
with(a) { … }
<하위클래스>
- 형태 Apple : Fruit()
- open class Fruit { ~
- 하위클래스는 상위클래스 멤버 모두 상속
- override를 쓰려면 상위에 open class
-->
<상속>
- 확장과 상속의 차이
상속에서는 인스턴스의 실제 타입(Apple? Fruit?)으로 호출 대상이 결정
확장은 변수의 타입에 따라 정적으로() 호출 대상이 결정 - 상속 시 주의할 점
상속한 곳과 함수 형태(파라미터 수, 타입 모두) 일치해야 함
final을 통해 추가 상속 제한 가능 final override fun start() {~ //나 이미 상속 끝났다~ - 프로퍼티(val, var 변수들)의 상속
하위 클래스 본문에 구현을 넣어 상속 가능
주생성자 파라미터로 class Person(override val name: String): Entity() 이런 식으로 상속 가능 - 불변 프로퍼티를 가변 프로퍼티로 상속 가능
val -> override var - protected 접근 제어자
상속에서 강력하다. 날 상속해야만 쓸 수 있어. - 초기화의 순서는 어떻게 될까?
상위 클래스 init 먼저, 그 다음 하위 클래스 init … - 상위 클래스 생성자로 데이터를 전달하고 싶다면?
주생성자 속에서는 그냥 :상위클래스(파라미터에 전달)
부생성자에서는 일단 상위클래스 뒤에 () 이거 없애고 constructor(파라미터들): super(파라미터 몇 개) { this.university = university }
- 퀴즈 : 상위 클래스가 여러 생성자를 지원하고 하위 클래스에서도 상위 클래스의 여러 생성자를 이용하고 싶다면 어떻게 해야 할까?
부생성자 여러 개!! - this 누출 문제
상속에서 아직 하위 클래스가 초기화되지 않은 상태인데 상위 클래스에서 생성되면서 호출이 되는 문제
<타입캐스팅>
- is, as
- 컴파일 시간의 타입 검사
컴파일 시에는 변수의 타입에 맞추어 연산 검사를 수행한다. 1,"2" 가 있으면 *2를 할 시 error - is Int -> ok. 자동 스마트 캐스트
- as -> 강제 스마트 캐스트
변환이 불가능할 시 as는 예외를, as?는 null을 반환
<오버라이딩과 추상화-빈껍데기>
- 오버라이딩 : super class에서 허용만 하면 서브클래스에서 은 형태의 함수 구현 가능
- 추상클래스 : 직접 인스턴스화 할 수 없고, 다른 클래스의 상위 클래스 역할만! 할 수 있는 클래스. 선언부만 있고 기능 구현 X
- 추상클래스의 생성자는 하위클래스의 위임 호출로만 호출 가능 super(name)
- 추상 멤버에 대한 제약
추상 프로퍼티는 초기화할 수 없다.
추상 함수에는 본문이 없다.
추상 프로퍼티와 함수 모두 명시적 타입 / 반환 타입이 필요하다. 타입 추론이 불가능하기 때문
추상 멤버는 기본적으로 open
<인터페이스>
- 다중 상속 가능.
- interface는 다른 interface만 상속 가능
- 비추상 class가 interface를 상속하면 interface의 모든 추상 멤버 구현해야 함
- 인터페이스의 함수나 프로퍼티는 구현 추가 가능. 구현을 했더라도 기본적으로 open~
- 메모리관련 프로퍼티(상태, 생성자) val age = 0 , return field 이런 거 안됨
- 다중상속 만약 같은 메소드가 된다면?
반드시 내부 구현이 존재해야 한다.
<Enum>
- Custom Member
- enum 클래스의 공통 멤버 (ordinal 과 name)
ordinal : 상수의 순번 --> 그래서 < 이거는 ordinal로 비교됨
name : 상수의 이름을 String 타입으로 반환
- valueOf() 와 values()
name 문자열로부터 상수를 얻어낼 때 valueOf() 함수 이용
values()는 모든 Enum 값이 들어있는 배열 반환해줌 - sealed
상속하는 하위 클래스의 정의를 같은 파일 내부로 제한. 함부로 상속하여 해당 class의 완결성을 저해할 수 없도록!
<클래스 동등성 비교 에 너무 좋은 DATA CLASS>
- equals / hashCode()
- data 클래스
toString() 자동으로 구현
자동으로 동등성 체크
copy()로 data class 복사
- 구조 분해 가능. 구조 분해 선언의 일부 무시도 가능. for문에서 강력 key, value 느낌
<Pair 과 Triple>
- 선언 방법
val pair = Pair(1,"Two")
val pair = 1 to "Two"
<부호 는 정수 타입>
UByte / UShort / UInt / ULong
- 부호 있는 타입과 타입변환 가능. 그 range안에서 돈다고 생각.
그런데 부호 있는 타입과 없는 타입 섞어서 연산은 안된다.
<컬렉션 타입>
- 종류
ArrayList
List
MutableList 삽입 삭제 대치 가능
HashSet
TreeSet
Map / HashMap / TreeMap
- 받을 때는 이렇게 받는다.
listOf()
sortedSetOf()
..
<파일과 I/O 스트림>
- I/O 관련 클래스
InputStream : Byte 단위로 입력을 읽어온다.
OutputStream: Byte 단위로 출력을 쓴다.
Reader : 문자 단위로 입력을 읽어온다.
Writer : 문자 단위로 입력을 쓴다.
<객체지향 프로그래밍 키워드>
- 객체는 별도의 역할이나 책임을 갖는 작은 독립
- 유지보수, 확장성
- 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다.
- 추상화 , 캡슐화 , 상속 , 다형성(오버로딩 오버라이딩)
<파일과 I/O Stream>
- use
- useLines -> 한 줄씩 읽는 sequence를 it으로 넘겨준다.
- forEachLine -> 매번 읽은 줄을 람다함수 파라미터로
<제너릭>
- 클래스나 함수에서 사용하는 자료형을 외부에서 지정
- T,U,V는 할당된 자료형으로 자동으로
- 별도로 타입 패러미터에 자료형 전달할 필요 없다